【题解】CF817E Choosing The Commander
\(\text{Solution:}\)
看到异或(以及E题的位置)想到\(01Trie.\)
考虑维护一个\(sum\)数组代表树上\(x\)点内有多少数字。那么添加操作每次走一个二进制位的时候更新\(sum+1,\)删除时更新\(sum-1.\)
考虑如何询问:
先读好题,上面说的是异或\(p_i\)小于\(l_i\)
所以这里\(l_i\)的二进制位起到了最主要的限制作用。
若\(l_i\)的这一位是\(1,\)则我们加上\(p_i\)这一位所对应相同的节点的\(sum\)(保证异或这一位是\(0,\)小于\(l_i\))
若\(l_i\)的这一位是\(0\)也就没有必要累加答案了,因为这里只有一种异或法则:令这一位为\(0.\)
考虑继续走:若\(l_i\)这一位是\(0\)我们就可以走到异或\(p_i\)这一位为\(0\)的位置;反之,由于我们之前计算过贡献,所以我们走到异或\(p_i\)这一位为\(1\)的位置。
时间复杂度\(O(n\log 10^8).\)
#include<bits/stdc++.h>
using namespace std;
int Q,T[3000010][2],n;
int tot=1,sum[3000010];
inline void insert(int v,int vv){
int p=1;
for(int i=30;i>=0;--i){
int x=v>>i&1;
sum[p]+=vv;
if(!T[p][x])T[p][x]=++tot;
p=T[p][x];
}
sum[p]+=vv;
}
int query(int ch,int v){
int ans=0,p=1;
for(int i=30;i>=0;--i){
int vp=v>>i&1;
int chp=ch>>i&1;
if(chp<vp)ans+=sum[T[p][0]];
if(vp&&chp)ans+=sum[T[p][1]];
if(!vp)p=T[p][chp];
else p=T[p][chp^1];
}
return ans;
}
int main(){
scanf("%d",&n);
while(n--){
int x,y,z;
scanf("%d%d",&x,&y);
if(x==1)insert(y,1);
if(x==2)insert(y,-1);
if(x==3){
scanf("%d",&z);
printf("%d\n",query(y,z));
}
}
return 0;
}
注意事项:\(Trie\)最好以\(1\)为根,有时候会出现一些不知道什么鬼的错误)