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;
}

posted @ 2018-08-14 22:24  谜のNOIP  阅读(114)  评论(0编辑  收藏  举报