luoguP4735 最大异或和(可持久化字典树)

原题链接

题意:

给定一个非负整数序列 {a},初始长度为n。

有 m 个操作,有以下两种操作类型:

A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 n+1。
Q l r x:询问操作,你需要找到一个位置 pp,满足l≤p≤r,使得:a[p]⊕a[p+1]⊕…⊕a[N]⊕x 最大,输出最大是多少。

思路:

  • 异或可以用前缀异或和维护,问题就变成了一个区间的数和x异或后的最大异或和
  • 考虑朴素做法,每次都将区间的数插入到 t r i e trie trie树上,贪心找最大值;时间和空间都不允许;
  • 可持久化数据结构可以查询历史区间的值,建立可持久化字典树,这样区间的问题就解决了;
  • 还有几个小细节:
  • 要事先插入0
  • 空间的计算:3e5个数,18位,每个位有0/1两种可能,大于 3 e 5 ∗ 2 ∗ 18 3e5*2*18 3e5218

代码:

const int maxn=6e5+10;
int n,m;///序列长度与询问个数
int root[maxn],idx;
int tr[maxn*25][2];
int sum[maxn];//前缀异或和
int max_id[maxn*25];//子树中下标的最大值
void insert(int i,int k,int p,int q){
	//第i棵字典树 k为二进制拆分后的位
	//p为上一个历史版本 q为当前版本
	if(k<0){//拆分成二进制后所有位都已处理完
		max_id[q]=i;return ;//记录子树下标的最大值,询问的l的条件
	}
	int v=sum[i]>>k&1;//记录值
	if(p) tr[q][v^1]=tr[p][v^1];//对立节点不修改,继承历史版本
	tr[q][v]=++idx;//开辟新节点
	insert(i,k-1,tr[p][v],tr[q][v]);//递归处理
	max_id[q]=max(max_id[tr[q][0]],max_id[tr[q][1]]);//类似线段树的pushup,用子节点的信息更新父节点
}

int query(int root,int val,int l){
	///从r向前查询,用max_id判断是否>=l,与val值贪心处理
	int p=root;
	for(int i=23;i>=0;i--){
		int v=val>>i&1;///这一位的值
		if(max_id[tr[p][v^1]]>=l) p=tr[p][v^1];//贪心的考虑,能取对立先取对立
		else p=tr[p][v];//不行取相同
	}
	return val^sum[max_id[p]];
}

int main(){
	n=read,m=read;
	root[0]=++idx;
	max_id[0]=-1;
	insert(0,23,0,root[0]);//处理和0异或的情况
	rep(i,1,n){
		int x=read;
		sum[i]=sum[i-1]^x;
		root[i]=++idx;
		insert(i,23,root[i-1],root[i]);
	}
	char op[2];
	while(m--){
		cin>>op;
		if(*op=='A'){
			int x=read;
			n++;
			sum[n]=sum[n-1]^x;
			root[n]=++idx;
			insert(n,23,root[n-1],root[n]);
		}
		else{
			int l=read,r=read,x=read;
			printf("%d\n",query(root[r-1],sum[n]^x,l-1));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

豆沙睡不醒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值