题意:每次插入/删除一个数,或询问当前所有数中异或上 \(p\) 之后小于 \(l\) 的有多少个。
看到动态最小化异或值的,我们首先想到 \(\text{Trie}\),我们先建立一棵 \(\text{Trie}\),在每个节点上保存当前节点子树的个数总和,就可以方便的 \(O(\log a)\) 插入或者删除。
然后考虑查询。我们发现,我们在递归到第 \(i\) 位时,如果 \(a_i\oplus p_i>l_i\),那么无论下面怎么走都不会到达结果。\(a_i\oplus p_i<l_i\) 时,此节点下方的所有值都满足要求。只有在 \(a_i\oplus p_i= l_i\) 时,我们需要继续前往当前节点的 \(a_i\) 节点确定答案。
而现在 \(l_i\) 的取值只有 \(0\) 和 \(1\) 两种,就可以直接分类讨论。
-
\(l_i=0\),那么取值为 \(p_i\) 的儿子会变成 \(0\),往下递归;取值为 \(\sim p_i\) 的儿子会变成 \(1\),直接干掉
-
\(l_i=1\),取值为 \(p_i\) 的儿子变成 \(0\),直接加上答案;取值为 \(\sim p_i\) 的儿子会变成 \(1\),往下递归
注意因为我们需要的是严格小于,所以不应当计算最终叶子节点的数
int n,t,x,y,trie[3000005][2],m,cnt[3000005];
inline void add(int x){
int cyr=0;
per(i,0,30){
int p=(x>>i&1);
if(!trie[cyr][p])trie[cyr][p]=++m;
cyr=trie[cyr][p],cnt[cyr]++;
}
}
inline void ers(int x){
int cyr=0;
per(i,0,30){
int p=(x>>i&1);
cyr=trie[cyr][p],cnt[cyr]--;
}
}
inline int qry(int p,int l){
int cyr=0,res=0;
per(i,0,30){
if(l>>i&1){
res+=cnt[trie[cyr][(p>>i&1)]];
if(!trie[cyr][!(p>>i&1)])return res;
cyr=trie[cyr][!(p>>i&1)];
}else{
if(!trie[cyr][p>>i&1])return res;
cyr=trie[cyr][p>>i&1];
}
}return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
rp(i,n){
cin>>t>>x;
if(t==1){
add(x);
}else if(t==2)ers(x);
else {
cin>>y;
cout<<qry(x,y)<<'\n';
}
}
return 0;
}
//Crayan_r
标签:cyr,cnt,int,res,Commander,trie,CF818E,Choosing,取值
From: https://www.cnblogs.com/jucason-xu/p/17152196.html