题目大意:
给 N N 个数, ; 有 M M 个询问,每次询问给出一个 ,求当前数列异或(XOR) x x 之后的 值( mex m e x 值指的是数列中未出现的最小自然数,例如 mex([5,1,1,0])=2 m e x ( [ 5 , 1 , 1 , 0 ] ) = 2 )。
Note : 每次询问之后原数列都会改变,即当前询问是基于上一个询问的。
题目链接:
数据范围:
1≤N,M≤3⋅105 1 ≤ N , M ≤ 3 ⋅ 10 5
0≤ai≤3⋅1050≤x≤3⋅105 0 ≤ a i ≤ 3 ⋅ 10 5 0 ≤ x ≤ 3 ⋅ 10 5
解题思路:
首先,若有数列 {0,1,2,⋯,2k−1} { 0 , 1 , 2 , ⋯ , 2 k − 1 } ,且有整数 x x 满足 ,那么整个数列异或 x x 后的结果依旧是 ; 例如 {0,1,2,3} { 0 , 1 , 2 , 3 } 异或 2 之后得 {2,3,0,1} { 2 , 3 , 0 , 1 } 。
其次,异或( ⊕ ⊕ ) 满足结合律,即 (x⊕y)⊕z=x⊕(y⊕z) ( x ⊕ y ) ⊕ z = x ⊕ ( y ⊕ z ) ; 所以,不必照着题意改变原数列,只需将原数列 XOR 询问的前缀异或值就可以了。
219−1=524287>3⋅105, 2 19 − 1 = 524287 > 3 ⋅ 10 5 , 令 全集 S={0,1,2,⋯,219−1} S = { 0 , 1 , 2 , ⋯ , 2 19 − 1 } , A={a1,a2,⋯,aN} A = { a 1 , a 2 , ⋯ , a N } (此处假设原数列已去重); A A 的补集令为 ; 对于当前的询问 x x ,要求的 ; 又因为 A⊕x+B⊕x=(A+B)⊕x=S⊕x=S A ⊕ x + B ⊕ x = ( A + B ) ⊕ x = S ⊕ x = S ,所以,所求的 mex(A′)=min(S−A⊕x)=min(B⊕x) m e x ( A ′ ) = min ( S − A ⊕ x ) = min ( B ⊕ x ) 。
综上,对于一个询问 x x , 就相当于在上述 集合中找一个值使得它 异或( ⊕ ⊕ ) x x 的值 最小;这种求 异或最值的问题 可以用 01Trie 树解决,将原数列 中没有出现的数(即上述的 B B ) 建成 01Trie 树,查询所要求的值即可。
Tips:要覆盖小于 3e5 3 e 5 的所有自然数,MaxN 要开到 6e5 6 e 5 (上面算过的),WA了好多发!
AC代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 600000;
int n, m;
bool flag[MaxN + 5];
struct Trie {
int Node[20 * MaxN + 5][2], val[20 * MaxN + 5];
int sz;
void Init() {
sz = 1;
memset(Node[0], 0, sizeof(Node[0]));
memset(val, 0, sizeof(val));
}
void insert(int x) {
int cur = 0;
for(int i = 20; i >= 0; i--) {
int idx = (x >> i) & 1;
if(!Node[cur][idx]) {
memset(Node[sz], 0, sizeof(Node[sz]));
Node[cur][idx] = sz;
sz++;
}
cur = Node[cur][idx];
}
val[cur] = x;
}
int query(int x) {
int cur = 0;
for(int i = 20; i >= 0; i--) {
int idx = (x >> i) & 1;
if(Node[cur][idx])
cur = Node[cur][idx];
else cur = Node[cur][idx ^ 1];
}
return val[cur] ^ x;
}
};
Trie t;
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
t.Init();
memset(flag, 0, sizeof(flag));
for(int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
flag[x] = 1;
}
for(int i = 0; i <= MaxN + 1; i++) {
if(flag[i] == 0)
t.insert(i);
}
int num = 0;
for(int i = 1; i <= m; i++) {
int x;
scanf("%d", &x);
num = num ^ x;
printf("%d\n", t.query(num));
}
}
return 0;
}