题目传送门
题意:
定义 是一个序列中没有出现过的最小非负数。
给你 个数 , 个操作。
每次操作给出一个数 , 让 个数都异或 。输出 。
异或操作不是独立的,即序列在每次操作的更改都是对后面有影响的。
数据范围: 。
题解:
首先要简化一下题意。假如第一个操作的数是 ,第二个操作的数是 。设 异或是 。
那么第二次操作后的序列就是 个数异或 。因为异或满足结合律。
这样简化过后就可以认为每次操作时独立的了。
我们需要把这些数先去重,因为我们是根据区间内的个数是不是等于区间长度来判断区间是不是满的。
我们对这去重后的数建01字典树。
每次操作时,假如操作的数当前位是1,那么我们优先走 对应的子树。否则优先走 对应的子树。
然后判定区间是不是满的。
感受:
我本来以为是线段树做的。
学会了01字典树的用法。
感觉要想上2100,估计得把2100的题刷完。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 3e5 + 5 ;
const int maxm = 25 ;
int cnt = 0 , b[maxm] ;
int n , a[maxn] ;
int num[maxn * maxm] ;
int p[maxn * maxm][2] ;
int t[maxn] ;
void init(int x)
{
int now = 0 ;
for(int i = 22 ; i >= 1 ; i --)
b[i] = x % 2 , x /= 2 ;
for(int i = 1 ; i <= 22 ; i ++)
{
if(p[now][b[i]] == 0) p[now][b[i]] = ++ cnt ;
now = p[now][b[i]] ;
num[now] ++ ;
}
t[22] = 1 ;
for(int i = 21 ; i >= 1 ; i --) t[i] = t[i + 1] * 2 ;
}
void solve(int x)
{
int now = 0 ;
int ans = 0 ;
for(int i = 22 ; i >= 1 ; i --)
b[i] = x % 2 , x /= 2 ;
for(int i = 1 ; i <= 22 ; i ++)
{
int l = p[now][0] , r = p[now][1] ;
int y = num[l] , z = num[r] ;
int h = t[i] ;
if(b[i] == 0)
{
if(y < h)
ans = ans * 2 + 0 , now = p[now][0] ;
else
ans = ans * 2 + 1 , now = p[now][1] ;
}
else
{
if(z < h)
ans = ans * 2 + 0 , now = p[now][1] ;
else
ans = ans * 2 + 1 , now = p[now][0] ;
}
if(now == 0)
{
for(int j = i + 1 ; j <= 22 ; j ++) ans *= 2 ;
break ;
}
}
printf("%d\n" , ans) ;
}
int main()
{
int x , q , last = 0 ;
scanf("%d%d" , &n , &q) ;
for(int i = 1 ; i <= n ; i ++) scanf("%d" , &a[i]) ;
sort(a + 1 , a + n + 1) ;
n = unique(a + 1 , a + n + 1) - a ;
for(int i = 1 ; i <= n - 1 ; i ++) init(a[i]) ;
while(q --)
{
int x ;
scanf("%d" , &x) ;
x ^= last ;
last = x ;
solve(x) ;
}
return 0 ;
}