传送门 - > (bzoj4260)
题目描述
给定一个含N个元素的数组A,下标从1开始,请找出下面式子的最大值:
(A[11]^ A[11+1] ^ … ^ A[r1]) + (A[12] ^ A[12+1]^ … ^A[r2])。
其中,(1<11<rl<l2<r2<N)。式中x^y表示x和y的按位异或运算。
输入
输入数据的第一行包含一个整数N,表示数组中的元素个数。
第二行包含N个整数A1,A2,…,AN。
输出
输出一行包含给定表达式可能的最大值。
样例输入
5
1 2 3 1 2
样例输出
6
提示
对于100%的数据,(2 ≤ N ≤ 4*10^5),(0 ≤ Ai ≤ 10^9).
题解
题目要求求出两段区间异或和之和最大,且这两个区间是递增的,没有交集,那么可以求出前缀最大异或和lmax以及后缀最大异或和rmax,其中lmax[i]代表[1,i]中一段区间的最大异或值,rmax[i]代表[i,n]中一段区间的最大异或值
求出这两个值后,(ans=max(ans,lmax[i]+rmax[i+1]),1<=i<=n).
那么怎么求出lmax和rmax呢,很显然在求这两个数组时是可以使用trie树前缀异或和优化的,即trie树保存的是前缀异或和d[i],表示(1-i)的异或和.
我们知道trie树可以线性求出两个值的最大异或和,那么d[i]^trie树中的一个值x,其中(xin({d[j]|1<=j<i})),这样就可以求出[j,i]这段区间的异或和
假如当前我们需要求出lmax[i],且d[1]~d[i-1]均已插入trie树中,
那么lmax[i]=max(lmax[i-1],find(d[i-1]^a[i])).其中find()函数是trie的检索函数,是用来求出最大异或和的
至于rmax,倒着做一遍就好了
trie树数组最大要开12000000*2,不会爆内存的
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<string>
#include<ctime>
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define in(i) (i=read())
using namespace std;
const double delta=0.998;
typedef long long lol;
int read() {
int ans=0,f=1; char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0'; i=getchar();}
return ans*f;
}
int n,ans,tot;
int a[400010],d[400010],trie[12000010][2];
int lmax[400010],rmax[400010];
void insert(int x) {
int p=0;
for(int i=30;i>=0;i--) {
int c=x>>i&1;
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
}
}
int find(int x) {
int ans=0,p=0;
for(int i=30;i>=0;i--) {
int c=x>>i&1,o=c^1;
if(trie[p][o]) ans=ans<<1|1,p=trie[p][o];
else ans<<=1,p=trie[p][c];
}
return ans;
}
int main()
{
in(n);
for(int i=1;i<=n;i++) {
in(a[i]);
insert(d[i-1]);
lmax[i]=Max(lmax[i-1],find(d[i]=d[i-1]^a[i]));
}
memset(trie,0,sizeof(trie)),tot=0;
for(int i=n;i>=1;i--) {
insert(d[i+1]);
rmax[i]=Max(rmax[i+1],find(d[i]=d[i+1]^a[i]));
}
for(int i=1;i<=n;i++)
ans=Max(ans,lmax[i]+rmax[i+1]);
cout<<ans<<endl;
}