BZOJ-4260: Codechef REBXOR

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/C_13579/article/details/82668177

地址:https://www.lydsy.com/JudgeOnline/problem.php?id=4260

思路:求不相交的两个连续区间异或值的和的最大值,那么可以分别求出连续区间异或值的前缀最大值 L[i] 和后缀最大值 R[i],

这样 Max=max{L[i]+R[i+1]}

关于前缀最大值L[i],由于必须是连续区间,则可以利用前缀异或和 ml[i]来建立 01字典树。

因为 ml[l]^ml[r] = a[l+1,r]的异或和,那么以a[i]为区间右端点的区间异或最大值就为对 ml[i]查找树的最大值Find(ml[i])

这样就 求L[i]=max(L[i-1],Find(ml[i]),对于 R[i]同理处理即可

Code :

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAX_N=400005;
const int MAX_S=12800005;
int n;
int a[MAX_N];
int ml[MAX_N],mr[MAX_N];	//异或前缀和,异或后缀和 
int Trie[MAX_S][2],num;
bool Sum[MAX_S];
int L[MAX_N],R[MAX_N];

void Insert(int x);	//将前缀和当做元素插入 
int Find(int x);
int main()
{
	scanf("%d",&n);
	Insert(0);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		ml[i]=ml[i-1]^a[i];
		Insert(ml[i]);
		L[i]=max(Find(ml[i]),L[i-1]);
	}
	memset(Trie,0,sizeof(Trie));
	memset(Sum,0,sizeof(Sum));
	num=0;
	Insert(0);
	for(int i=n;i>=1;--i)
	{
		mr[i]=mr[i+1]^a[i];
		Insert(mr[i]);
		R[i]=max(Find(mr[i]),R[i+1]);
	}
	int ans=0;
	for(int i=1;i<n;++i)
		ans=max(ans,L[i]+R[i+1]);
	printf("%d\n",ans);
	
	return 0;
}

void Insert(int x)
{
	int u=0,t;
	for(int i=31;i>=0;--i)
	{
		t=(x>>i)&1;
		if(!Trie[u][t])	Trie[u][t]=++num;
		u=Trie[u][t];
		Sum[u]=1;
	}
}

int Find(int x) 
{
	int u=0,t,p,res=0;
	for(int i=31;i>=0;--i)
	{
		t=(x>>i)&1;
		p=Trie[u][!t];
		if(p&&Sum[p]){
			res=res|(1<<i);
			u=p;
		}else	u=Trie[u][t];
	}
	
	return res;
}

猜你喜欢

转载自blog.csdn.net/C_13579/article/details/82668177