( ext{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(nlog 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
",query(y,z));
}
}
return 0;
}
注意事项:(Trie)最好以(1)为根,有时候会出现一些不知道什么鬼的错误)