题意:每次插入/删除一个数,或询问当前所有数中异或上 \(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