题目:
问题很简单,现在有一个数组a1,a2,a3……an。你的任务就是找到一个连续子段[l,r],使得al^al+1^……^ar达到最大。
Input
多组输入,每组有两行。第一行有一个整数n(1<=n<=10^5),表示数组的元素个数。第二行有n个元素,依次表示数组的元素。(0<=ai<=10^6)
Output
每组输出一行,这行仅一个数字。表示最大的连续子段异或值。
Sample Input
Raw
5
1 2 3 4 5
5
2 3 2 3
初次接触字典树,看了很多大神的代码,因为我根本不知道为什么字典树可以处理异或,现在有了一点点理解所以写一下博客强化一下记忆。
题目分析:
首先要明白异或是什么,异或就是相对于二进制的一种运算,相同为0,不同为1;
对于这道题我们先处理一个前缀,即1~2,1~3,~~~~,1~n,的异或值;对于l~~r区间的异或值就为1~~r的异或值在异或1~~l-1的异或值;现在这个问题就变成了在前缀数组里找两个数求他们异或值得 最大;
因为每一位只有0,1,两种状态,所以就像一个二叉,我们题目的数据范围为10^4所以25位就可以了,我们先建立一个字典树,当我们对字典树进行访问时就对答案进行比较看谁最大,我们从最高位进行判断,如果最高位有不同(相同1,不同0)的那么这个答案肯定是最优的,我们再进行判断下一位,如果最高位相同我们就直接判断下一位,又看看下一位是否相同,或者不同,下面代码判断相同不同我用的bool类型如果有不同的话,我肯定是建立了节点的 如果相同就找不到!k这个节点。
废话多都说了,上码。
AC代码
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int a[100005];
int tree[1000005][2];
int clk=0;
void add(int qq)
{
int i,len=25,rt=0;
bool k;
for (i=len;i>=0;i--)
{
k=qq&(1<<i);
if(!tree[rt][k])
tree[rt][k]=++clk;
rt=tree[rt][k];
}
}
int que(int qq)
{
int rt=0,len=25,i;
int an=0;
bool k;
for (i=len;i>=0;i--)
{
k=qq&(1<<i);
if(tree[rt][!k])
{
an=an+(1<<i);
rt=tree[rt][!k];
}
else
rt=tree[rt][k];
}
return an;
}
int main ()
{
int n,i,k;
while (scanf ("%d",&n)!=EOF)
{
memset(tree,0,sizeof(tree));
clk=0; int ans=0;
scanf ("%d",&a[0]);
for (i=1;i<n;i++)
{
scanf ("%d",&k);
a[i]=k^a[i-1];
}
add(0);
for (i=0;i<n;i++)
{
add(a[i]);
int maxn=que(a[i]);
ans=max(ans,maxn);
}
printf ("%d\n",ans);
}
return 0;
}