这道题还是很值得整理的,每次插入一个数或者删除一个数,每次给你两个数字x,h 问你给定的插入的集合中的 a 与x异或值大于等于h的到底有几个?
想到用trie树之后我们记得之前做过一个一堆数中选择两个数字让他们的异或值最大,是贪心的做
每插入一个数字我们就查询当前已经插入的数字和它异或值最大的结果(每次在走路径的时候优先走二进制位反着的就行了)
这个题问我们的 发现可以标记已经走过的路径,然后删除一个数的话也就是删除路径上的标记
查询的话,我们需要这样做,我们从高位往低位来做,首先h的二进制位数为0的时候我们要加上可以走到1的数目,然后顺着它的路径继续向前走,因为我们求的是严格大于它的数字,我们走的时候是1的话就想办法顺着它走就可以了,还是很有教育意义的一个题目
// Problem: 异星突击
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/61657/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define int long long
#define ls u<<1
#define rs u<<1|1
#define inf LLONG_MIN
#define _inf LLONG_MAX
#define fs first
#define se second
#define pb push_back
using namespace std;
const int N = 1e5*32;
int T;
int tr[N][3],idx;
int cnt[N];
void insert(int x,int c){
int p =0;
for(int i=31;~i;i--){
int j = x>>i&1;
if(!tr[p][j]) tr[p][j] = ++idx;
p = tr[p][j];
cnt[p]+=c;
}
}
int query(int x,int h){
int p =0;
int res = 0;
for(int i=31;~i;i--){
int j = x>>i&1;
if(h>>i&1) p = tr[p][!j];
else{
res+=cnt[tr[p][!j]];
p = tr[p][j];
}
if(!p)return res;
}
return res;
}
void solve()
{
int n,hp;
cin>>n>>hp;
while(n--){
int id,x,h;
cin>>id>>x;
if(id==0){
insert(x,1);
}else if(id==1){
insert(x,-1);
}else{
cin>>h;
int num = query(x,h);
cout<<num<<"\n";
if(!num)
hp--;
}
}
cout<<hp<<"\n";
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
//cin>>T;
T = 1;
while(T--)solve();
return 0;
}