题目描述
给出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; }