bzoj 4888 [Tjoi2017]异或和 树状数组
题面
解法
枚举最后的二进制位,假设是第\(k\)位
问题就转化成有多少段和二进制意义下第\(k\)位是1
不妨转化成前缀和
变成有多少对\(i,j(i>j)\),使得\(s_i-s_j\)的第\(k\)位是1
我们可以枚举\(k\),假设\(s_i\)的第\(k\)位是1
如果\(s_i-s_j\)的第\(k\)位是1,那么存在两种情况:
1.\(s_j\)的第\(k\)位是0,且不存在减法退位这种情况
2.\(s_j\)的第\(k\)位是1,出现了减法退位
那么我们只要比较前\(k-1\)位的情况即可
用权值树状数组统计一下即可
\(s_i\)的第\(k\)位是0同理
那么只要开两个权值树状数组就可以解决问题
时间复杂度:\(O(n\ log^2\sum a_i)\)
代码
#include <bits/stdc++.h>
#define M 1000010
#define N 100010
using namespace std;
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, a[N], s[N], f[2][M];
int lowbit(int x) {return x & -x;}
void modify(int k, int x) {
for (int i = x + 1; i <= s[n] + 1; i += lowbit(i))
f[k][i]++;
}
int query(int k, int x) {
int ret = 0;
for (int i = x + 1; i; i -= lowbit(i))
ret += f[k][i];
return ret;
}
int calc(int l, int r, int k) {
return query(k, r) - query(k, l);
}
int main() {
int ans = 0; read(n);
for (int i = 1; i <= n; i++)
read(a[i]), s[i] = s[i - 1] + a[i];
for (int k = 0; (1 << k) <= s[n]; k++) {
int sum = 0, lim = (1 << k) - 1;
for (int i = 0; i <= n; i++) {
int t = (s[i] >> k) & 1, las = s[i] & lim;
if (t) sum = (sum + calc(-1, las, 0) + calc(las, lim, 1)) % 2, modify(1, las);
else sum = (sum + calc(-1, las, 1) + calc(las, lim, 0)) % 2, modify(0, las);
}
if (sum) ans |= 1 << k;
memset(f, 0, sizeof(f));
}
cout << ans << "\n";
return 0;
}