Time Limit: 2000 MS Memory Limit: 65536 KB
Description
问题很简单,现在有一个数组a1,a2,a3……an。你的任务就是找到一个连续子段[l,r],使得al^al+1^……^ar达到最大。
Input
多组输入,每组有两行。第一行有一个整数n(1<=n<=10^5),表示数组的元素个数。第二行有n个元素,依次表示数组的元素。(0<=ai<=10^6)
Output
每组输出一行,这行仅一个数字。表示最大的连续子段异或值。
Sample Input
5
1 2 3 4 5
5
2 3 2 3 2
Sample Output
7
3
【解题思路】 问题都很清楚,遍历所有的连续子段异或值即可,但很不幸,这样会TLE;于是,我们转换一下,要求连续子段[l,r]区间的异或值,我们可以设a[i]=a1^a2^….^ai,则连续子段[l,r]的异或值就等于a[r]^a[l-1](两个相同的数异或会抵消),这样我们就只需要遍历r之前的a[i]与a[r]异或,但这样还是会超时,怎样快速找出最大值呢?这里我们就可以引入字典树,将a[i]变成二进制,压入字典树,a[r]也变成二进制,从最高位开始寻找,尽量找与其不同的值,最后就可以得到最大值。
【AC代码】
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int a[100005],trz[200005][2],vis;
void build(int id,int x)
{
for(int i=20;i>=0;i--)
{
if(x&(1<<i))
{
if(!trz[id][1])
{
trz[id][1]=++vis;
memset(trz[vis],0,sizeof(trz[vis])); //可以避免浪费时间清除不用清除的空间
}
id=trz[id][1];
}
else
{
if(!trz[id][0])
{
trz[id][0]=++vis;
memset(trz[vis],0,sizeof(trz[vis]));
}
id=trz[id][0];
}
}
}
int found(int id,int x)
{
int sum=0;
for(int i=20;i>=0;i--)
{
if(x&(1<<i))
{
if(trz[id][0])
{
id=trz[id][0];
sum+=(1<<i);
}
else
id=trz[id][1];
}
else
{
if(trz[id][1])
{
id=trz[id][1];
sum+=(1<<i);
}
else
id=trz[id][0];
}
}
return sum;
}
int main()
{
int n,i;
while(scanf("%d",&n)!=EOF)
{
memset(trz[0],0,sizeof(trz[0]));
vis=0;build(0,0);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]^=a[i-1];
build(0,a[i]);
}
int ans=0;
for(i=1;i<=n;i++)
ans=max(ans,found(0,a[i]));
printf("%d\n",ans);
}
return 0;
}