CF1847C 题解
题意简述
给定起始长度为
其中
求任意多次操作后,序列中元素的最大值。
重要结论
本题即求序列
证明上述结论,即证以下两点:
- 新加入的元素可以取到原序列
a 的最大子段异或和。 - 新加入的元素只可能为原序列
a 的任一子段异或和。
对于第
对于第
按位异或运算有如下性质:
故式子
即
因为
故
即第一点得证。
对于第
故第二点得证。
代码实现
计算序列最大子段异或和是一个经典问题。这里我们使用 01Trie 实现。
首先
直接
我们使用 01Trie 进行优化,具体步骤如下:
对于第
因为我们要求两者异或结果尽量大,所以我们在查询时贪心地尽量选择二进制位不同的分支。
每次插入与查询的时间复杂度均为
代码如下:
#include <iostream>
using namespace std;
const int max_n = 1e5 + 10;
int trie[max_n * 10][2], nums[max_n * 10], tot;
int a[max_n];
int pre[max_n];
int dp[max_n];
void insert(const int value);
int search(const int value);
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n + 2; ++i) {
dp[i] = 0;
for (int j = 0; j < 10; ++j) {
trie[(i - 1) * 10 + j][0] = trie[(i - 1) * 10 + j][1] = 0;
nums[(i - 1) * 10 + j] = 0;
}
}
tot = 0;
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] ^ a[i];
}
insert(pre[0]);
for(int i = 1; i <= n; ++i)
{
dp[i] = max(dp[i - 1], search(pre[i]));
insert(pre[i]);
}
printf("%d\n", dp[n]);
}
return 0;
}
void insert(const int value) {
int cur = 0;
for (int i = 9; i >= 0; --i) {
int bit = (value >> i) & 1;
if (!trie[cur][bit]) {
trie[cur][bit] = ++tot;
}
cur = trie[cur][bit];
}
nums[cur] = value;
}
int search(const int value) {
int cur = 0;
for (int i = 9; i >= 0; --i) {
int bit = (value >> i) & 1;
if (trie[cur][bit ^ 1]) {
cur = trie[cur][bit ^ 1];
}
else {
cur = trie[cur][bit];
}
}
return value ^ nums[cur];
}