Loading

AGC057C 做题记录

题面看着很吓人!但是经过了一步步的思考,切完后再来看,其实也不过如此。

纪念一下独立切的铜牌构造题。

由于有 \(+1\) 操作,考虑反着建立 01-trie,即以最低位作为第一个分支。这样 \(+1\) 操作相当于对最右边的一条链上每个点执行左右儿子交换。

考虑 trie 树上每个叶子挂着对应数值在 \(A\) 中的位置,我们的目标状态为:每个叶子挂着的数等于对应数值。

不管是异或还是 \(+1\) 操作,都只能交换左右儿子,所以无解的判定是容易的:进行一次 DFS,判断当前点 \(u\) 与目标状态的 \(v\) 子树是否合法,每次判断 \(u\) 的子树中最左边的叶子在 \(v\) 的左子树还是右子树,然后递归判定。

这样一遍 DFS 我们还能顺便求出 \(t_u=0/1\) 表示为了到达目标状态,点 \(u\) 是否需要交换左右儿子。

为了方便表述,我们以“\(t_u\) 的改变”来代替点 \(u\) 交换左右儿子的操作。那么,我们的目标是把所有点的 \(t\) 变为 \(0\)

我们现在有两种方法:

  • 异或一个数,使得同一层内的点 \(t\) 值全部取反,或全都不改变。

  • \(+1\),使得最右边一条链上所有点的 \(t\) 值全部取反。

注意到对于一条链,如果我们通过异或操作把它“旋转”到最右边,进行一次 \(+1\) 操作,再旋转回来,等价于我们可以令这条链上的 \(t\) 值全部取反。

所以我们思考操作若干条链来达成目标。

但是不一定有解。在操作之前,我们可以先异或一个值 \(v\),使得一些层的 \(t\) 值取反。

具体的,枚举最后一层(不是叶子,是叶子上面的一层)是否先提前取反,然后依次往上枚举每一层。

\(d_u\) 表示被链覆盖的次数的奇偶性,有 \(d_u = d_{lson(u)}\operatorname{xor} d_{rson(u)}\),合法的条件为 \(d_u\operatorname{xor} t_u = 0\)

如果该层全部点都满足 \(d_u\operatorname{xor} t_u = 1\),那么先提前把这一层所有 \(t\) 取反。

然后这题就做完了,操作次数显然远远小于 \(10^6\)

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define fi first
#define se second
#define pir pair <ll, ll>
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn = 3e5 + 10, mod = 1e9 + 7;
ll n, a[maxn], b[maxn], ans[maxn << 2], len;
ll trie[maxn * 18][2], tot = 1, t[maxn * 18], fa[maxn * 18];
vector <ll> vec[20];
void dfs(ll bit, ll u, ll v, ll uw, ll vw) {
	if(bit == n) {
		if(b[uw] != vw) {
			puts("No");
			exit(0);
		} return;
	}
	if(b[uw] & (1 << bit)) {
		dfs(bit + 1, trie[u][0], trie[v][1], uw, vw | (1 << bit));
		dfs(bit + 1, trie[u][1], trie[v][0], uw | (1 << bit), vw);
		t[u] = 1;
	} else {
		dfs(bit + 1, trie[u][0], trie[v][0], uw, vw);
		dfs(bit + 1, trie[u][1], trie[v][1], uw | (1 << bit), vw | (1 << bit));
	}
}
ll tag;
ll query(ll u) {
	ll val = 0;
	for(ll i = n - 2; ~i; i--) {
		val |= (trie[fa[u]][1 ^ ((tag >> i) & 1)] == u) << i;
		u = fa[u];
	} return val;
}
void add1() {
	ll p = 1;
	for(ll i = 0; i < n; i++) {
		swap(trie[p][0], trie[p][1]);
		p = trie[p][(tag >> i) & 1];
	}
}
ll msk, d[maxn * 18], ok;
int main() {
	scanf("%lld", &n); vec[0].pb(1);
	for(ll i = 0; i < (1 << n); i++) {
		scanf("%lld", a + i); b[a[i]] = i;
		ll p = 1;
		for(ll j = 0; j < n; j++) {
			ll c = (a[i] >> j) & 1;
			if(!trie[p][c]) {
				trie[p][c] = ++tot;
				vec[j + 1].pb(tot);
				fa[tot] = p;
			}
			p = trie[p][c];
		}
	}
	dfs(0, 1, 1, 0, 0);
	for(ll i = 0; i < 2; i++) {
		msk = (i << n - 1), ok = 1;
		for(ll j: vec[n - 1]) d[j] = t[j] ^ i;
		for(ll j = n - 2; ~j; j--) {
			ll x = 0, gd = 0;
			for(ll k: vec[j]) {
				d[k] = t[k] ^ x;
				if(d[k] ^ d[trie[k][0]] ^ d[trie[k][1]]) {
					if(gd) {
						ok = 0;
						break;
					}
					x = 1, d[k] ^= 1;
				}
				gd = 1;
			}
			msk |= (x << j);
			if(!ok) break;
		}
		if(!ok) continue;
		ans[++len] = tag = msk;
		for(ll j: vec[n - 1])
			if(d[j]) {
				ll val = query(j);
				val ^= (1 << n - 1) - 1;
				ans[++len] = val, tag ^= val;
				add1(), ans[++len] = -1;
			}
		ans[++len] = tag ^ msk;
		puts("Yes");
		printf("%lld\n", len);
		for(ll i = 1; i <= len; i++)
			printf("%lld ", ans[i]);
		return 0;
	}
	puts("No");
	return 0;
}
posted @ 2024-05-11 10:14  Lgx_Q  阅读(12)  评论(0编辑  收藏  举报