题目描述
原题来自:CODECHEF September Challenge 2015 REBXOR
给定一个含 NNN 个元素的数组 AAA,下标从 111 开始。请找出下面式子的最大值:(A[l1]⨁A[l1+1]⨁…⨁A[r1])+(A[l2]⨁A[l2+1]…⨁A[r2]),其中1≤l1≤r1<l2≤r2≤N 1\le l_1\le r_1<l_2\le r_2\le N1≤l1≤r1<l2≤r2≤N,x⨁yx\bigoplus yx⨁y 表示 xxx 和 yyy 的按位异或。
输入格式
输入数据的第一行包含一个整数 NNN,表示数组中的元素个数。
第二行包含 NNN 个整数 A1,A2,…,AN。
输出格式
输出一行包含给定表达式可能的最大值。
样例
样例输入
5
1 2 3 1 2
样例输出
6
样例解释
满足条件的(l1,r1,l2,r2)(l1,r1,l2,r2)(l1,r1,l2,r2)有:(1,2,3,3),(1,2,4,5),(3,3,4,5)(1,2,3,3),(1,2,4,5),(3,3,4,5)(1,2,3,3),(1,2,4,5),(3,3,4,5)。
数据范围与提示
对于 100100100 的数据,2≤N≤4×105,0≤Ai≤1092\le N \le 4\times 10^5, 0\le A_i\le 10^92≤N≤4×105,0≤Ai≤109。
思路:
1、要找出(A[l1]^A[l1+1]^…^A[r1])+(A[l2]^A[l2+1]…^A[r2]),只要找到两个A[l]^…^A[r]就可以了。
2、利用字典树可以寻找从1~n上最大的A[l]^…^A[r](1≤l≤r≤N)。
原理:比如一棵树插入了2(010),5(101),7(111),分别查询与6(110),3(011)做^运算结果的的最大数,
查找时利用尽量走相反方向的字符指针的策略。
3、所以将问题转化一下:
从左到右求1~n求最大的A[l]^…^A[r](1≤l≤r≤N)用l[i]表示,再从右到左求n~1的最大的A[l]^…^A[r](1≤l≤r≤N),用r[i]
表示。
最后求出max(l[i],r[i+1]);即为结果。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 5e+5;
const int Z = 2;
int vc[N<<5][Z],l[N],r[N],a[N];
int tot;
void it(int x)
{
int u=1,i;
for(i=1<<30;i;i>>=1)
{
int c=(x&i)?1:0;
if(!vc[u][c]) vc[u][c]=++tot;
u=vc[u][c];
}
}
int f(int x)
{
int u=1,i,ans=0;
for(i=1<<30;i;i>>=1)
{
int c=(x&i)?0:1;
if(vc[u][c])
{
u=vc[u][c];
ans+=i;
}
else u=vc[u][!c];
}
return ans;
}
int main(void)
{
int now,n,i,ans;
tot=1;
memset(vc,0,sizeof(vc));
scanf("%d",&n);
it(now=0);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
now^=a[i];
it(now);
l[i]=max(l[i-1],f(now));
}
tot=1;
memset(vc,0,sizeof(vc));
it(now=0);
for(i=n;i>=1;i--)
{
now^=a[i];
it(now);
r[i]=max(r[i+1],f(now));
}
ans=0;
for(i=1;i<n;i++)
ans=max(ans,l[i]+r[i+1]);
printf("%d\n",ans);
return 0;
}