題目傳送門
題意:
定義 是一個序列中沒有出現過的最小非負數。
給你 個數 , 個操作。
每次操作給出一個數 , 讓 個數都異或 。輸出 。
異或操作不是獨立的,即序列在每次操作的更改都是對後面有影響的。
數據範圍: 。
題解:
首先要簡化一下題意。假如第一個操作的數是 ,第二個操作的數是 。設 異或是 。
那麼第二次操作後的序列就是 個數異或 。因爲異或滿足結合律。
這樣簡化過後就可以認爲每次操作時獨立的了。
我們需要把這些數先去重,因爲我們是根據區間內的個數是不是等於區間長度來判斷區間是不是滿的。
我們對這去重後的數建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 ;
}