题解:P11008 『STA - R7』异或生成序列

思路

对于异或,有一个小性质,即 \(x\;\oplus\;y\;\oplus\;x=y\)

题目中我们也能运用这个性质,考虑到对于相邻的两个 \(b_{i}\),它们的表达式中有一个共同的 \(p_{i}\),所以异或两个相邻的 \(b_{i}\) 就会把那个出现了两次的 \(p_{i}\) 消除,即:

\[\begin{aligned} b_{i}\;\oplus\;b_{i+1} = p_{i}\;\oplus\;p_{i+1}\;\oplus\;p_{i+1}\;\oplus\; p_{i+2} = p_{i}\;\oplus\;p_{i+2} \end{aligned} \]

但是这样有什么意义呢?意义很重大啊!因为我们知道 \(b_{i}\;\oplus\;b_{i+1}\) 的值,所以可以有意义的将式子移项:

\[\begin{aligned} p_{i+2}=p_{i}\;\oplus\;(b_{i}\;\oplus\;b_{i+1}) \end{aligned} \]

同时显然的 \(p_{i+1}=p_{i}\;\oplus\;b_{i}\)

这个式子告诉我们,只要有 \(p_{i}\) 的值,就可以求出 \(p_{i+1}\) 以及以后的 \(p_{i}\),那么我们只要枚举 \(p_{1}\) 就行。

考虑暴力判断 \(p_{1}\) 生成的序列的可行性,这样是 \(O(n^2)\) 的,考虑优化。

我们知道,实际上判就只是判这三个因素:

1.序列的所有元素大于 \(0\)

2.序列的所有元素小于 \(n+1\)

3.序列没有重复的元素。

三条件实际对于所有有解输入都成立,因为每个 \(p_{i}(i>1)\) 的表达式形如:

\[\begin{aligned} p_{i}=p_{1}\;\oplus\;(b_{1}\;\oplus\;...\;\oplus\;b_{i-1}) \end{aligned} \]

如果有解,那么 \((b_{1}\;\oplus\;...\;\oplus\;b_{i-1})\) 一定对于任何的 \(i>1\) 都不重复,不然对于任何一个解,都会出现重复的情况。所以 \((b_{1}\;\oplus\;...\;\oplus\;b_{i-1})\) 互不重复,异或任意 \(p_{1}\) 自然也不重复。

考虑条件一二,因为所有元素大于一个数等价于最小元素大于那个数,所有元素小于一个数等价于最大元素小于那个数,所以考虑快速求出上式最大最小值的方法。

由于上式为位运算,是在每位进行的,如果想要贪心的最大或最小,就需要依次决定每位,从高位到低位。

所以考虑记录 \(b\) 每一个的前缀异或和,然后求最大最小值时,贪心的从高往低考虑取 \(0\) 还是 \(1\) 即可。具体的,这个做法我们用 trie 树实现。

解法

维护 trie 树的解法。

首先考虑维护一颗由 \(b\) 前缀构成的树,然后每次可以枚举 \(p_{1}\),运用 trie 树判断运用 \(p_{1}\) 生成的 \(p\) 序列是否合法,即最大值是否小于 \(n+1\),最小值是否大于 \(0\),如果合法,按思路构造即可。最后通过队列一层一层清空即可,这样只会清空不为 \(0\) 的节点。

Code

#include <bits/stdc++.h>
using namespace std;
const int N=42'000'000;
int son[N][2],idx; 
void gun() {
	queue<int> q;
	q.push(0);
	while(q.size()) {
		auto t=q.front();
		q.pop();
		for(int i=0;i<2;i++) if(son[t][i]) q.push(son[t][i]),son[t][i]=0;
	}
}
void insert(int x) {
	int p=0;
	for(int i=19;i>=0;i--) {
		int u=(x>>i)&1;
		if(!son[p][u]) son[p][u]=++idx;
		p=son[p][u];
	}
}
int qry_max(int x) {
	int p=0,ans=0;
	for(int i=19;i>=0;i--) {
		int u=(x>>i)&1;
		if(son[p][!u]) {
			ans+=(1<<i);
			p=son[p][!u];
		}
		else p=son[p][u];
	}
	return ans;
}
int qry_min(int x) {
	int p=0,ans=0;
	for(int i=19;i>=0;i--) {
		int u=(x>>i)&1;
		if(son[p][u]) p=son[p][u];
		else {
		    ans+=(1<<i);
		    p=son[p][!u];
		}
	}
	return ans;
}
int n;
int a[N];
signed main() {
	int tt;
	cin>>tt;
	while(tt--) {
		cin>>n;idx=0;
		for(int i=1;i<n;i++) cin>>a[i];int cnt=0;
		for(int i=1;i<n;i++) {cnt^=a[i],insert(cnt);}
		for(int i=1;i<=n;i++)
			if(qry_max(i)<=n&&qry_min(i)>=1) {
				cnt=0;
				cout<<i<<' ';
				for(int j=1;j<n;j++) {
					cnt^=a[j];
					cout<<(cnt^i)<<' ';
				}
				break;
			}
		gun();
		puts("");
	}
	return 0;
} 
posted @ 2024-08-27 11:39  PM_pro  阅读(15)  评论(0)    收藏  举报