【题解】CF817E Choosing The Commander

题目戳我

\(\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\)为根,有时候会出现一些不知道什么鬼的错误)

posted @ 2020-11-26 17:17  Refined_heart  阅读(102)  评论(0编辑  收藏  举报