BZOJ 3261: 最大异或和

这个题,用的可持久化字典树来维护区间异或和
感觉用法跟主席树差不多,会主席树,可持久化字典树应该不难学

记 res[i] 为 [1 , i ] 的 a[] (程序中我使用的是ans[]) 的异或和
则求max(res[p]res[n]x) (l-1<=p<=r-1)

每次加点将trie树的这条链的权都+1
修改当然是新建一个结点
然后查询的时候判断一个结点存在,只要做区间减法判权是否非0
即若 trie[r].size - trie[l-1].size =0则该结点不存在

查询的贪心策略,其实很简单,res[n]^x的二进制某个位置 ind 为1,我们就找可持久化字典树区间对应位置是否存在0,存在,则这个位置的异或值为1 << ind,不存在0就只能选择1(因为这个区间肯定是有数的),所以这个位置的异或值为0,如果位置ind是0的话,相反即可。

实现的时候数列开始加入一个数0会比较好处理

我的代码:

#include<cstdio>
using namespace std;

const int maxn=600000+100;

struct Trie{
	
	int l,r;
	int size;
}trie[maxn*24];
int tot,root[maxn];
int ans[maxn],res[maxn];

inline void Update(int &New,int Old,int v){
	
	New=++tot;
	trie[New]=trie[Old];
	trie[New].size=trie[Old].size+1;
	int now=New,pre=Old;
	for(int i=24;i>=0;i--){
		
		int tmp=(v>>i)&1;
		int nodel=trie[pre].l,noder=trie[pre].r;
		if(tmp){
			
			trie[now].l=nodel;
			trie[now].r=++tot;
			trie[tot]=trie[noder];
			trie[tot].size=trie[noder].size+1;
			now=tot,pre=noder;
		}
		else{
			
			trie[now].r=noder;
			trie[now].l=++tot;
			trie[tot]=trie[nodel];
			trie[tot].size=trie[nodel].size+1;
			now=tot,pre=nodel;
		}
	}
}

inline int Query(int l,int r,int v){
	
	int query=0;
	if(l<0) l=0;
	for(int i=24;i>=0;i--){
		
		int Lnodel=trie[l].l,Lnoder=trie[l].r;
		int Rnodel=trie[r].l,Rnoder=trie[r].r;
		int tmp=(v>>i)&1;
		if(tmp){
			
			if(trie[Rnodel].size-trie[Lnodel].size) query+=(1<<i),l=Lnodel,r=Rnodel;
			else  l=Lnoder,r=Rnoder; 
		}
		else{
			
			if(trie[Rnoder].size-trie[Lnoder].size) query+=(1<<i),l=Lnoder,r=Rnoder;
			else  l=Lnodel,r=Rnodel; 
		}
	}
	return query;
} 

int main(){
	
	int n,m;
	scanf("%d%d",&n,&m);
	Update(root[0],root[0],0);
	for(int i=1;i<=n;i++){
		
		scanf("%d",&ans[i]);
		res[i]=res[i-1]^ans[i];
		Update(root[i],root[i-1],res[i]);
	}
	while(m--){
		
		char ch=getchar();
		while(ch<'A' || ch>'Z') ch=getchar();
		if(ch=='A'){
			
			n++;
			scanf("%d",&ans[n]);
			res[n]=res[n-1]^ans[n];
			Update(root[n],root[n-1],res[n]);
		}
		else{
			
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			printf("%d\n",Query(root[l-2],root[r-1],res[n]^x));
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ITKaven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值