BZOJ-4260: Codechef REBXOR

[文章目录]

Description

给定序列{a},求两个不相交子区间,使得子区间的亦或和的和最大。2 ≤ N ≤ 4*10^5,0 ≤ Ai ≤ 10^9。

要是只选一个子区间的话好做,直接维护亦或前缀,对于每个前缀在trie树上找到最优的解,时间复杂度O(n*loga)。
那么对于两个区间,我们可以枚举两个区间在哪里分开。那么只需要处理每个前后缀选出一个子区间的最大亦或。和只选一个子区间方法相同,只需要记录前后缀最值即可,时间复杂度同上。
别忘了0也是前后缀

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,a[401000],L[401000],R[401000];
inline char nc()
{
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2) ? EOF : *p1++;
}
inline void read(int &x)
{
    char ch=nc();
    while(ch<'0'||ch>'9') ch=nc();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=nc();
}
int t[13000000][2];
int tot=1,rot=1;
inline void insert(int x)
{
    int p=rot;
    for(int i=30;~i;--i)
    {
        if(!t[p][(x&(1<<i))?1:0]) t[p][(x&(1<<i))?1:0]=++tot;
        p=t[p][(x&(1<<i))?1:0];
    }
}
inline int query(int x)
{
    int p=rot,re=0;
    for(int i=30;~i;--i)
    {
        if(t[p][(x&(1<<i))?0:1]) re|=1<<i,p=t[p][(x&(1<<i))?0:1];
        else p=t[p][(x&(1<<i))?1:0];
    }
    return re;
}
void clear(int x)
{
    if(!x) return ;
    clear(t[x][0]);
    clear(t[x][1]);
    t[x][0]=t[x][1]=0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) read(a[i]);
    insert(0);
    for(int i=1;i<=n;++i)
    {
        L[i]=max(L[i-1],query(a[i]));
        insert(a[i]);
    }
    clear(rot); tot=1; insert(0);
    for(int i=n;i;--i)
    {
        R[i]=max(R[i+1],query(a[i]));
        insert(a[i]);
    }
    int ans=0;
    for(int i=1;i<n;++i) ans=max(ans,L[i]+R[i+1]);
    printf("%d",ans);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注