字典树处理《异或》

题目:
问题很简单,现在有一个数组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 
#include 
#include 
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<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<if(tree[rt][!k])
        {
            an=an+(1<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;iscanf ("%d",&k);
            a[i]=k^a[i-1];
        }
        add(0);
        for (i=0;iint maxn=que(a[i]);
            ans=max(ans,maxn);
        }
        printf ("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(线段树,树)