P3760 [TJOI2017] 异或和 简单的单 log 做法
一个简单的
推荐一个类似题:gym 102538E,处理技巧一致。
区间和问题,在前缀和上考虑。异或问题,每一位分开考虑。
对于前缀和
因此,只需要在处理每一位时,都求出这样的交替区间的前缀和,查询时就能
具体来说,设
最后还有一点:我们需要去掉
总复杂度
暴力代码(不加前缀和优化):
int n; cin >> n;
vi a(n + 1), s(n + 1);
F (i, 1, n) cin >> a[i];
partial_sum(all(a), s.begin());
F (i, 1, n) cnt[s[i]]++;
int ans = 0;
F (j, 0, 19) {
int mx = s[n];
int res = 0;
F (i, 1, n)
F (v, 0, mx) {
if (abs(s[i] - v) & (1 << j)) {
res += cnt[v];
}
}
res /= 2;
ans += (res & 1) << j;
}
F (i, 1, n) ans ^= s[i];
cout << ans << '\n';
用前缀和优化后:
constexpr int maxn = 1e5 + 10, maxv = 1e6 + 10;
int cnt[maxv], scnt[maxv];
int f[maxv], g[maxv];
#define val(p, t) ((t) >= 0 ? p[(t)] : 0)
#define val2(p, t) ((t) <= mx ? p[(t)] : p[mx])
#define val3(p, t) ((t) <= mx ? p[(t)] : 0)
// 处理边界
void OoO_main() {
int n; cin >> n;
vi a(n + 1), s(n + 1);
F (i, 1, n) cin >> a[i];
partial_sum(a.begin(), a.end(), s.begin());
const int mx = s[n];
F (i, 1, n) cnt[s[i]]++;
partial_sum(cnt, cnt + mx + 1, scnt);
int ans = 0;
F (j, 0, 19) {
int res = 0;
fill(f, f + mx + 1); // 滚动数组节省空间
F (i, 0, mx) {
f[i] = val(f, i - (1 << (j + 1))) + val(scnt, i - (1 << j)) - val(scnt, i - (1 << (j + 1)));
}
DF (i, mx, 0) {
g[i] = val3(g, i + (1 << (j + 1))) + val2(scnt, i + (1 << (j + 1)) - 1) - val2(scnt, i + (1 << j) - 1);
}
F (i, 1, n) {
res += g[s[i]] + f[s[i]];
}
res /= 2;
ans += (res & 1) << j;
}
F (i, 1, n) ans ^= s[i]; // 可能需要特殊处理一下区间和是 s[i] - s[0] 的情况
cout << ans << '\n';
}
update on 2025.4.26:感谢管理员 Little_Cart 指出错误,并增加了一些补充说明。