P3760 [TJOI2017] 异或和
[TJOI2017] 异或和
题目描述
在加里敦中学的小明最近爱上了数学竞赛,很多数学竞赛的题都是与序列的连续和相关的。 所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的简单。
但今天小明遇到了一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和(即字串和),还要快速的求出这些连续和的异或值。
小明很快的就求出了所有的连续和,但是小明要考考你,在不告诉连续和的情况下,让你快速求是序列所有连续和的异或值。
输入格式
第一行输入一个 \(n\),表示这序列的数序列
第二行输入 \(n\) 个非负整数 \(a_1,a_2 \dots a_n\) 代表这个序列
输出格式
输出这个序列所有的连续和的异或值
样例 #1
样例输入 #1
3
1 2 3
样例输出 #1
0
提示
【样例解释】
序列 \(1\) \(2\) \(3\) 有 \(6\) 个连续和,它们分别是 \(1\) \(2\) \(3\) \(3\) \(5\) \(6\),则 \(1 \text{ xor } 2 \text{ xor } 3 \text{ xor } 3 \text{ xor } 5 \text{ xor } 6 = 0\)
【数据范围】
对于 \(20\%\) 的数据,\(1\le n \le 100\)
对于 \(100\%\) 的数据,\(1\le n \le 10^5\),\(\sum\limits_{i=1}^na_i \le 10^6\)
对于求答案的异或和这类问题,一般要对每一位分别考虑,看这一位出现的次数是否是奇数次,然后最终计算答案。
考虑如何计算第 \(k\) 位的贡献,题目要求所有连续子序列的和,那么快速的计算一端区间的和可以用前缀和来计算,对于一段区间 \([i, j]\) 的和,可以使用 \(s_j - s_{i-1}\) 来计算。如果 \(s_j - s_i\) 的第 \(k\) 位为 \(1\) ,那么可以有两种情况:
- 如果 \(s_j\) 的低 \(k-1\) 位是大于等于 \(s_i\) 的低 \(k-1\) 位,那么当 \(s_j\) 的第 \(k\) 位和 \(s_i\) 的第 \(k\) 位不同的时候,\([i, j]\) 的区间和的第 \(k\) 位是 \(1\)
- 如果 \(s_j\) 的低 \(k-1\) 位是小于 \(s_i\) 的低 \(k-1\) 位,那么当 \(s_j\) 的第 \(k\) 位和 \(s_i\) 的第 \(k\) 位相同的时候,\([i, j]\) 的区间和的第 \(k\) 位是 \(1\)
证明可以考虑手摸几个数据,注意 \(s_j\) 一定是 大于等于 \(s_i\) 的
然后可以使用两个权值树状数组,分别考虑第 \(k\) 位的为 \(1\) 和 \(0\) 的时候,低 \(k-1\) 位的大小。
CODE
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, a, b) for (int i(a); i <= b; ++ i )
#define dec(i, a, b) for (int i(b); i >= a; -- i )
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
template <typename T>
class fenwick {
public:
vector<T> fenw;
int n;
fenwick(int _n) : n(_n) {
fenw.resize(n);
}
void modify(int x, T v) {
while (x < n) {
fenw[x] += v;
x |= (x + 1);
}
}
T get(int x) {
T v{};
while (x >= 0) {
v += fenw[x];
x = (x & (x + 1)) - 1;
}
return v;
}
T get(int l, int r) {
return get(r) - get(l - 1);
}
};
constexpr int N = 1E5 + 10;
int n, a[N], s[N];
void solve() {
cin >> n;
rep (i, 1, n) cin >> a[i], s[i] = s[i - 1] + a[i];
int ans = 0;
int m = 1000010;
for (int i = 0; i < 20 && (1 << i) <= s[n]; i ++ ) {
vector<fenwick<int> > f(2, fenwick<int>(m));
int now = 0;
f[0].modify(0, 1);
for (int j = 1; j <= n; j ++ ) {
int bit = s[j] >> i & 1; //当前位是 1 or 0
int low = s[j] & ((1 << i) - 1);
auto sum = f[bit ^ 1].get(low) + f[bit].get(low + 1, m - 1);
now ^= (sum & 1);
f[bit].modify(low, 1);
}
if (now & 1) ans |= 1 << i;
}
cout << ans << "\n";
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
solve();
return 0;
}