Nikitosh 和异或

https://loj.ac/problem/10051

题目描述

  给出N个数的序列A,要求求出两段连续的子序列(互不重叠),他们内的数异或后值的和最大。

思路

  这道题其实是The XOR Largest Pair的进阶版,我们知道暴力枚举显然无法实现,所以就要用到一些优化。首先我们考虑如果选出一段连续子序列最大如何做。我们已知一个很显然的性质x^x=0,0^x=x,所以对于一段子序列我们可以用类似维护前缀和的方法,用s[r]^s[l-1]取出[l,r]这一段的异或值,因为前几个数可以异或为0。不过暴力枚举子序列的复杂度我们仍无法承受,但我们知道快速从一堆数中求出一对数异或值最大。所以这就等价于从s[1..r-1]找出和s[r]异或起来最大值,再取个max即可,我们也可用字典树维护。

  接下来就是两段不重叠的序列,我们知道这一定由两段[l1,r1],[l2,r2],因此他们一定可以从一个端点分为两部分,所以我们只要正着做一遍,倒着做一遍,得到两个数组l,r,那么答案就是l[i]+r[i+1]中的最大值。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=4e5+5;
int ch[MAXN<<5][3],tot,a[MAXN],l[MAXN],r[MAXN];
void insert(int x)
{
    int u=1;
    for(int i=1<<30;i;i>>=1)
    {
        int num=(x&i)?1:0;
        if(!ch[u][num])ch[u][num]=++tot;
        u=ch[u][num];
    }
}
int find(int x)
{
    int u=1,ans=0;
    for(int i=1<<30;i;i>>=1)
    {
        int num=(x&i)?0:1;
        if(ch[u][num])
        {
            ans+=i;
            u=ch[u][num];
        }
        else u=ch[u][!num];
    }
    return ans;
}
int main() 
{
    int n,sum=0;
    scanf("%d",&n);
    tot=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum=sum^a[i];
        insert(sum);
        l[i]=max(l[i-1],find(sum));
    }
    memset(ch,0,sizeof(ch));
    sum=0;tot=1;
    for(int i=n;i>0;i--)
    {
        sum=sum^a[i];
        insert(sum);
        r[i]=max(r[i+1],find(sum));
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,l[i]+r[i+1]);
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fangbozhen/p/11628475.html
今日推荐