题目大意:
给你 n 个数,让你求两个不相交的区间元素异或后的和的最大值。本题中 n 的上限是 4*10^5.
首先看下异或的性质,关于异或我们知道: 0^a=a , a^a=0 。前 i 个数的异或和前 j 个数的异或相异或: pre[i]^pre[j] = a[i+1]^a[i+2]^……^a[j],(i<j)。异或的后缀和类似。
于是我们可以先求出异或的前缀 pre[i]和后缀和 suf[i]。dp[i]表示前 i个数中任意区间异或的最大值,可以依次求与 pre[i] 相异或结果的最大值,然后把 pre[i] 插入到 01字典树中。
这样对于每个 pre[i] 他会和之前的 i-1 个异或前缀和的共有部分所抵消,也就相当于是求任意区间的异或结果的最大值了。这样求出了一个区间,同理可利用后缀和求出另一个区间。
怎么保证两个区间不相交呢?可以通过使前后两个区间一个为不包含第 i个数的前部分区间,一个是包含第 i 个数的后部分区间就可以了。
AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<queue>
#include<map>
#include<iomanip>
#include<math.h>
#include<sstream>
using namespace std;
typedef long long ll;
typedef double ld;
const int MAXN = 400010;
int tol;
int val[MAXN*32];
int tree[MAXN*32][2];
//dp[i]表示前 i个数中任意区间异或的最大值
//pre[i]表示前 i个数的异或结果,即前缀和
//suf[i]表示第 i个数之后的互素异或结果,即后缀和
int dp[MAXN],pre[MAXN],suf[MAXN];
void init()
{
//初始化
tol=1;
tree[0][0]=tree[0][1]=0;
}
void insert(int x)
{
int u=0;
for(int i=32; i>=0; i--)
{
int v=(x>>i)&1;
if(!tree[u][v])
{
tree[tol][0]=tree[tol][1]=0;
val[tol]=0;
tree[u][v]=tol++;
}
u=tree[u][v];
}
val[u]=x;
}
int query(int x)
{
int u=0;
for(int i=32; i>=0; i--)
{
int v=(x>>i)&1;
if(tree[u][v^1])
u=tree[u][v^1];
else
u=tree[u][v];
}
return x^val[u];
}
int main()
{
int i,j,n,ans,a[MAXN];
while(~scanf("%d",&n))
{
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
pre[0]=suf[n+1]=0;
for(i=1; i<=n; i++)
pre[i]=pre[i-1]^a[i]; //前缀和
for(i=n; i>=1; i--)
suf[i]=suf[i+1]^a[i]; //后缀和
memset(dp,0,sizeof(dp));
init(); //第一次初始化
insert(pre[0]);
for(i=1; i<=n; i++)
{
//求出 dp[i],即前 i个数的任意区间异或的最大值
dp[i]=max(dp[i-1],query(pre[i]));
insert(pre[i]);
}
init(); //第二次初始化
ans=0;
insert(suf[n+1]);
for(i=n; i>=1; i--)
{
//求出最终结果
//query(suf[i])+dp[i-1]即 i 之后的任意区间异或值和 i之前的任意区间异或值之和
ans=max(ans,query(suf[i])+dp[i-1]); //dp[i-1] 保证了两个区间不相交
insert(suf[i]);
}
printf("%d\n",ans);
}
return 0;
}