思路 :
假设这道题求的是 两个不交区间的区间和的和 的最大值, 输入的数组是 a[i]
那么利用动态规划, f[i] 表示以 i 结尾的最大区间和, g[i] 表示以 i 开头的最大区间和。
则 f[i]=max(f[i-1]+a[i], a[i]), g[i]=max(g[i+1]+a[i], a[i]);
然后, 我们将 f[i] 更新, 使其表示 max(f[1], f[2], ..., f[i]), 同理, g[i]=max(g[i+1], g[i+2], ..., g[n]);
显而易见, 答案就是 : 枚举 i , 取 max{f[i]+g[i+1] } 。
但是这道题是区间异或运算, 对于区间加, f [ i ] f[i] f[i] 由 f [ i − 1 ] + a [ i ] f[i-1]+a[i] f[i−1]+a[i] 转换, 或者由 a [ i ] a[i] a[i] 转换, 这两种方式肯定有一个是最优的。异或运算则不同, f [ i − 1 ] ∧ a [ i ] f[i-1]\wedge a[i] f[i−1]∧a[i] 或 a [ i ] a[i] a[i] 却不一定是最好的了, 所以这道题需要字典树来查询哪个更好
如何操作字典树 :
令 pre[i] 表示 1 ~ i 中最大异或和区间, query(x) 为字典树查询与 x 最大异或结果的函数 。
枚举到第 i 个数的时候, 令 s=a1^a2^...^ai, 将 s 插入字典树, 则 pre[i]=max(pre[i-1], query(s));
其中 query(s) 就能查以 i 结尾的最大连续异或区间, 因为我们前面已经插入了一些不同的 S。
注意要在字典树建立时插入一个 0, 用于初始化 pre[1] 。 在处理 suf[i] 时同理 pre[i]
AC代码
#include<bits/stdc++.h>
using namespace std;
int n;
int const N = 4e5+10;
int a[N], pre[N], suf[N];
struct Trie {
int idx, son[N*31][2];
void mem() {
memset(son, 0, sizeof son);
idx=0;
}
void insert(int x) {
int p=0;
for(int i=30; i>=0; i--) {
int u=(x>>i&1);
if(!son[p][u]) son[p][u]=++idx;
p=son[p][u];
}
}
int query(int x) {
int p=0, res=0;
for(int i=30; i>=0; i--) {
int u=(x>>i&1);
if(son[p][!u]) {
res+=(1<<i);
p=son[p][!u];
} else {
p=son[p][u];
}
}
return res;
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i];
Trie trie;
trie.mem();
trie.insert(0);
int s=0;
for(int i=1; i<=n; i++) {
s^=a[i];
trie.insert(s);
pre[i]=max(pre[i-1], trie.query(s));
}
trie.mem();
trie.insert(0);
s=0;
for(int i=n; i>=1; i--) {
s^=a[i];
trie.insert(s);
suf[i]=max(suf[i+1], trie.query(s));
}
int res=0;
for(int i=1; i<n; i++) res=max(res, pre[i]+suf[i+1]);
cout<<res;
return 0;
}