Description
给定一个长度为 (n) 的数列 (a_1,a_2,...,a_n),选定四个整数 (l_1,r_1,l_2,r_2(1le l_1le r_1<l_2le r_2le n)),则函数 (operatorname{F}(l_1,r_1,l_2,r_2)) 的计算方式如下:
对于所有符合条件的 ((l_1,r_1,l_2,r_2)) ,求 (operatorname{F}(l_1,r_1,l_2,r_2)) 的最大值。
Hint
(1le nle 4 imes 10^5, a_i in [0,10^9])
Solution
设 (operatorname{lmax}(i)) 为前 (i) 个数中选取连续的某一段,得到的最大异或和是多少。对于 (operatorname{rmax}(i)) 同样定义。
假设我们已经计算好了 (operatorname{lmax}) 和 (operatorname{rmax}) ,那么答案就是 (maxlimits_{i=1}^{n-1}{operatorname{lmax}(i)+operatorname{rmax}(i+1)})。
选现在的问题就是如何求 (operatorname{lmax}) 和 (operatorname{rmax}) 了,以 (operatorname{lmax}) 为例:
令 (operatorname{pre}(i) = a_1oplus a_2oplus cdots oplus a_i)(其实就是前缀异或和)。
那么对于 (operatorname{pre}(i)) 我们需要找一个 (i) 之前的一个前缀异或和与之异或来得到一段异或和,并且要求这个异或得到的值尽可能大。
求最大异或和,很自然的想到 Trie 树:只要把 (operatorname{pre}(1,2,cdots ,i-1)) 和 (0) 存入 Trie 中,就可以找到这个最大异或值。
另外,这个最大异或值还不一定比上一个优,所以还需与 (operatorname{lmax}(i)) 取最大值。
综上: (operatorname{lmax}(i) = max(operatorname{lmax}(i-1), operatorname{query}(operatorname{pre}(i)))) ,其中 (operatorname{query}(x)) 是指在 Trie 中找到的与 (x) 的异或产生的最大值。
同理: (operatorname{rmax}(i) = max(operatorname{rmax}(i+1), operatorname{query}(operatorname{suf}(i)))) ,其中 (operatorname{suf}) 为后缀异或和。
时间复杂度 (O(nlog U)),(U) 为值域。
注意事项:
- 第二次使用 Trie 前记得清空。
- 计算 (operatorname{suf},operatorname{rmax}) 时记得从 (n) 到 (1)。
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : CodeChef REBXOR Nikitosh and xor
*/
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 4e5 + 5;
const int S = N << 6;
int ch[S][2];
int total = 1;
void reset() {
memset(ch, 0, sizeof ch);
total = 1;
}
void insert(int val) {
for (register int cur = 1, i = 30; ~i; --i) {
int c = val >> i & 1;
if (!ch[cur][c]) ch[cur][c] = ++total;
cur = ch[cur][c];
}
}
int search(int val) {
int cur = 1, ret = 0;
for (register int i = 30; ~i; --i) {
int c = val >> i & 1;
if (ch[cur][!c]) c ^= 1, ret |= 1 << i;
cur = ch[cur][c];
}
return ret;
}
int ary[N], n;
int pre[N], suf[N];
int lmax[N], rmax[N];
signed main() {
cin >> n;
for (register int i = 1; i <= n; i++)
cin >> ary[i];
for (register int i = 1; i <= n; i++)
pre[i] = pre[i - 1] ^ ary[i];
for (register int i = n; i >= 1; i--)
suf[i] = suf[i + 1] ^ ary[i];
reset(), insert(0);
for (register int i = 1; i <= n; i++)
lmax[i] = max(lmax[i - 1], search(pre[i])), insert(pre[i]);
reset(), insert(0);
for (register int i = n; i >= 1; i--)
rmax[i] = max(rmax[i + 1], search(suf[i])), insert(suf[i]);
int ans = 0;
for (register int i = 1; i < n; i++)
ans = max(ans, lmax[i] + rmax[i + 1]);
cout << ans << endl;
return 0;
}