BZOJ 4260 Codechef REBXOR trie树+树状数组

来源:互联网 发布:阿里云架设代理服务器 编辑:程序博客网 时间:2024/04/30 16:08

Description

Input

输入数据的第一行包含一个整数N,表示数组中的元素个数。
第二行包含N个整数A1,A2,…,AN。

Output

输出一行包含给定表达式可能的最大值。

Sample Input

5
1 2 3 1 2

Sample Output

6

HINT

满足条件的(l1,r1,l2,r2)有:(1,2,3,3),(1,2,4,5),(3,3,4,5)。

对于100%的数据,2 ≤ N ≤ 4*105,0 ≤ Ai ≤ 109。










传送门
好题……不怎么难。。

先考虑只取一段的情况,
首先对原来的数组作xor的前缀和。
我们知道xor也是满足这样的性质的:
a[x]^a[x+1]^……^a[y]=sum[x-1]^sum[y]
我们可以枚举连续序列的尾部y,那么也就是说要找出一个x,
使得sum[x-1]^sum[y]最大。
xor不像加法或者减法,对于不同的数字,和它异或起来大的数字是不同的。
这个时候去考虑一下二进制方面,
假如sum[y]=0011(二),那么sum[x-1]尽量是谁呢?
当然是1100(二)了。
每一位都尽量找不同的那一位即可。。
那假如没有sum[x-1]=1100呢?1000和0100谁更优呢。。
二进制的从后往前第x位带有2^(x-1)的权值,
也就是011111<100000...
所以我们从高位到低位,能有对应的就选择这个对应的,不然只好选择另一个。

这个过程可以用trie树实现,每个节点连向儿子有两种边,一种是0,一种是1;
比如走1的边,意义就是二进制上这一位是1.
每次处理完sum[x],就把sum[x]转化为二进制插入到trie里就好了。
假如一个节点u,sum[y]的目前位置的二进制位数字是p,
如说u的son[p^1]非空,那么u走向son[p^1],不然u走向son[p],
最后把数字加起来,return的就是和sum[y]xor最优的了。。


-> v ->这个地方竟然说了这么多……
那题目要求两段不重复的,要怎么办呢?
首先对于第一段,枚举一个结尾的话,很容易就用刚才的方法求出最优值了;
而对于第二段,如果是枚举结尾i,我们可以发现不一定是刚才的方法最优了……
因为我们要找的是{以k结尾的第一段的最大值+sum[i] xor sum[j-1]}
可以用下图来表示:
可以发现这个时候就有点不可做了……
但是我们可以发现:第一段枚举结尾来确定,因为第一段的之前的部分对后面没有了影响;
很容易地,能够想到优化:
枚举第二段的开头,也就是图中的j,
然后在trie树里,我们可以直接找出来一个最优的sum[i](因此我们要从尾往头线扫);
而对于F[k],我们仍然是取最大的,这个F[k]的最大值我们用树状数组维护即可。

没有任何难度啊喂我写这么多……




#include<bits/stdc++.h>using namespace std;const int    N=400005,    MAXLEN=31;int n,len,cnt;int ejz[MAXLEN+5],a[N],sum[N];int tr[N],f[N],trie[N*MAXLEN][2];void update(int x,int y){    for (int i=x;i<=n;i+=i&-i) tr[i]=max(tr[i],y);}int getmaxf(int x){    int y=0;    for (int i=x;i;i-=i&-i) y=max(y,tr[i]);    return y;}void jzzh(int x){    len=0;    while (x)        ejz[++len]=(x&1),x>>=1;    int tmp=len+1;    for (int i=tmp;i<=MAXLEN;i++) ejz[i]=0;    len=MAXLEN;}void insert(int x){    int now=0;    jzzh(x);    for (int i=len;i;i--)        if (trie[now][ejz[i]]) now=trie[now][ejz[i]];            else now=trie[now][ejz[i]]=++cnt;}int get_xor_max(int x){    int now=0,an=0;    jzzh(x);    for (int i=len;i;i--){        if (trie[now][!ejz[i]])            now=trie[now][!ejz[i]],an+=(!ejz[i])*(1<<(i-1));         else            now=trie[now][ejz[i]],an+=ejz[i]*(1<<(i-1));    }    return an;}void Pre(){    sum[0]=0;    for (int i=1;i<=n;i++) sum[i]=sum[i-1]^a[i];    cnt=0,insert(0);    f[1]=sum[1],insert(sum[1]);    for (int i=2;i<=n;i++){        f[i]=sum[i]^get_xor_max(sum[i]);        insert(sum[i]);    }    for (int i=1;i<=n;i++) update(i,f[i]);}void solve(){    cnt=0;    memset(trie,0,sizeof(trie));    insert(sum[n]);    int ans=0;    for (int j=n-1;j>1;j--){        int tmp=sum[j]^get_xor_max(sum[j]);        ans=max(ans,getmaxf(j)+tmp);        insert(sum[j]);    }    printf("%d\n",ans);}int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    Pre();    solve();    return 0;}