Nikitosh 和异或
https://loj.ac/problem/10051
题目描述
给出\(N\)个数的序列\(A\),要求求出两段连续的子序列(互不重叠),他们内的数异或后值的和最大。
思路
这道题其实是The XOR Largest Pair的进阶版,我们知道暴力枚举显然无法实现,所以就要用到一些优化。首先我们考虑如果选出一段连续子序列最大如何做。我们已知一个很显然的性质\(x\bigoplus x=0\),\(0\bigoplus x=x\),所以对于一段子序列我们可以用类似维护前缀和的方法,用\(s[r]\bigoplus 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;
}