\(\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\)爲根,有時候會出現一些不知道什麼鬼的錯誤)