一道很有启发性的题目。
考虑到由于是自然数对的比较,所以我们不能直接用 (01 ext{Trie}) 。
但是考虑到我们可以将 ( ext{Trie}) 转换成有 (4) 个出边的,每一个出边表示数对当前位的 (01) 情况。
然后你考虑查询,对于当前位置,存在一种大于的情况,两种等于的情况,一种小于的情况。
考虑到大于的情况我们可以直接计数,等于的情况就需要递归了,但是由于我们每一层都需要递归两次,效率过低,这该怎么办?
考虑到我们大于情况是只需要数量的,我们可以考虑不建儿子,这样的话对于每一个点,我们可以将 (00) 和 (11) 合并,(01) 和 (10) 合并。这样无论是怎样的情况,等于的递归就只需要进入一个节点即可。
然后每一个节点只需要储存一个数在当前位是 (0) 或 (1) 的个数就行了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,K=63;
int n;
struct Trie{
int rt,tot;Trie(){rt=tot=1;}
struct Node{int son[2],size[2];}tr[N*K];
void insert(long long x,long long y,int z){
int u=rt;
for(int i=K;i>=0;--i){
int tx=((x>>i)&1),ty=((y>>i)&1);
if(!tr[u].son[tx^ty]) tr[u].son[tx^ty]=++tot;
u=tr[u].son[tx^ty],tr[u].size[tx]+=z;
}
}
int query(long long x,long long y){
int u=rt,res=0;
for(int i=K;i>=0;--i){
int tx=((x>>i)&1),ty=((y>>i)&1);
res+=tr[tr[u].son[tx^ty^1]].size[tx^1];
u=tr[u].son[tx^ty];
}
return res;
}
}t;
signed main(){
cin>>n;
for(int i=1;i<=n;++i){
int opt;long long x,y;
scanf("%d%lld%lld",&opt,&x,&y);
if(opt==1) t.insert(x,y,1);
else if(opt==2) t.insert(x,y,-1);
else printf("%d
",t.query(x,y));
}
return 0;
}