luogu P4735 最大异或和

背景:

h e h e . . . hehe... hehe...

题目传送门:

https://www.luogu.org/problemnew/show/P4735

题意:

一个序列,支持两种操作加入一个元素 x x x或给出 l , r , x l,r,x l,r,x,询问在 [ l , r ] [l,r] [l,r]中找到一个点 p p p,使得 a p ⊕ a p + 1 ⊕ a p + 2 ⊕ . . . ⊕ a n ⊕ x a_p⊕a_{p+1}⊕a_{p+2}⊕...⊕a_{n}⊕x apap+1ap+2...anx最大,求这个的最大值。

思路:

back to summary \text{back to summary} back to summary.
异或的解决方法一般有线性基和 01Trie \text{01Trie} 01Trie
这是一道动态的问题,需要动态的方式,当然是动态 01Trie \text{01Trie} 01Trie,即可持久化 01Trie \text{01Trie} 01Trie
其实我们知道可持久化线段树(一种实现方式是主席树),运用类似的思想构建一棵可持久化 01Trie \text{01Trie} 01Trie
我们可以求出异或前缀 p r e i pre_i prei
a p ⊕ a p + 1 ⊕ a p + 2 ⊕ . . . ⊕ a n ⊕ x a_p⊕a_{p+1}⊕a_{p+2}⊕...⊕a_{n}⊕x apap+1ap+2...anx

= p r e p − 1 ⊕ p r e n ⊕ x =pre_{p-1}⊕pre_{n}⊕x =prep1prenx

= p r e p − 1 ⊕ ( p r e n ⊕ x ) =pre_{p-1}⊕(pre_{n}⊕x) =prep1(prenx)
p r e n ⊕ x pre_{n}⊕x prenx是已知的,不妨设为 S S S
我们就转化成求 S ⊕ p r e p − 1 S⊕pre_{p-1} Sprep1的最大值,其中 p ∈ [ l , r ] p∈[l,r] p[l,r]
这等价于求 S ⊕ p r e p S⊕pre_{p} Sprep的最大值,其中 p ∈ [ l − 1 , r − 1 ] p∈[l-1,r-1] p[l1,r1]
注意一下 l = 1 l=1 l=1(即 l − 1 = 0 l-1=0 l1=0)时要特判一下。
还是递归版比较和谐。
over. \text{over.} over.

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
	int root[20000000],tot[20000000],pre[600010];
	int tr[20000000][2];
	int n,m,len=0;
void insert(int now,int last,int t,int x)
{
	if(t<0) return;
	int tmp=(x>>t)&1;
	tr[now][tmp]=++len;
	tr[now][tmp^1]=tr[last][tmp^1];
	tot[tr[now][tmp]]=tot[tr[last][tmp]]+1;
	insert(tr[now][tmp],tr[last][tmp],t-1,x);
}
int query(int now,int last,int t,int x)
{
	if(t<0) return 0;
	int tmp=(x>>t)&1;
	if(tot[tr[now][tmp^1]]<tot[tr[last][tmp^1]]) return (1<<t)|query(tr[now][tmp^1],tr[last][tmp^1],t-1,x); else return query(tr[now][tmp],tr[last][tmp],t-1,x);
}
int main()
{
	int l,r,x;
	char s[5];
	scanf("%d %d",&n,&m);
	root[0]=++len;
	insert(root[0],0,25,0);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		pre[i]=pre[i-1]^x;
		root[i]=++len;
		insert(root[i],root[i-1],25,pre[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s+1);
		if(s[1]=='A')
		{
			scanf("%d",&x);
			n++;
			pre[n]=pre[n-1]^x;
			root[n]=++len;
			insert(root[n],root[n-1],25,pre[n]);
		}
		else
		{
			scanf("%d %d %d",&l,&r,&x);
			l--,r--;
			printf("%d\n",query(!l?0:root[l-1],root[r],25,x^pre[n]));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值