1 条题解
-
0
思路与算法
先从左扫描。令前缀异或P_i=A_1异或...异或A_i,任何以i结尾的区间异或为P_i异或P_j(j<i)。把历史P_j插入31层二进制Trie,查询与P_i异或最大的值,维护截至i的最大区间异或left[i]。从右对称得到right[i]。最后枚举两段之间的分割点i,最大化left[i]+right[i+1]。
正确性说明
前缀异或抵消公共部分,故历史前缀查询恰涵盖所有以i结尾的非空区间。Trie从高位到低位优先选择相反位,是因为较高位的一位增益大于全部低位总和,因此查询最优。左右扫描取前缀或后缀最大值正确。任意合法两段之间至少有一个分割点,其值不超过该点左右最优之和;反过来每个分割点的两个最优区间必然非空且不交,故取最大值恰为答案。
复杂度
每次插入和查询31层,两个方向总O(31N),包括数组和Trie节点初始化;枚举分割点O(N)。空间O(31N),两次扫描复用同一Trie容量。
易错点
先查询再插入当前前缀,避免只靠空区间取值;初始插入0。两个子数组均须非空,分割点只能到N-1。两段和可达2^32-2,应使用64位输出。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct Trie { vector<array<int, 2>> t; Trie(int n) { t.reserve(min(31LL * n + 1, 14000000LL)); clear(); } void clear() { t.clear(); t.push_back({{0, 0}}); } void add(unsigned x) { int v = 0; for (int k = 30; k >= 0; k--) { int b = x >> k & 1; if (!t[v][b]) { t[v][b] = t.size(); t.push_back({{0, 0}}); } v = t[v][b]; } } unsigned get(unsigned x) { int v = 0; unsigned ans = 0; for (int k = 30; k >= 0; k--) { int b = x >> k & 1; if (t[v][b ^ 1]) { ans |= 1U << k; v = t[v][b ^ 1]; } else v = t[v][b]; } return ans; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; if (!(cin >> n)) return 0; vector<unsigned> a(n), l(n), r(n); for (auto &x : a) cin >> x; Trie t(n); t.add(0); unsigned x = 0, best = 0; for (int i = 0; i < n; i++) { x ^= a[i]; best = max(best, t.get(x)); l[i] = best; t.add(x); } t.clear(); t.add(0); x = best = 0; for (int i = n - 1; i >= 0; i--) { x ^= a[i]; best = max(best, t.get(x)); r[i] = best; t.add(x); } unsigned long long ans = 0; for (int i = 0; i + 1 < n; i++) ans = max(ans, (unsigned long long)l[i] + r[i + 1]); cout << ans << '\n'; }
- 1