【题解】CF817E Choosing The Commander

题目戳我

( ext{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(nlog 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
",query(y,z));
		}
	}
	return 0;
}

注意事项:(Trie)最好以(1)为根,有时候会出现一些不知道什么鬼的错误)

原文地址:https://www.cnblogs.com/h-lka/p/14043338.html