牛客小白月赛9-红球进黑洞(异或线段树)

题意:

操作 \(1\):求 \([l,r]\) 区间和。
操作 \(2\):区间 \([l,r]\) 的数异或上\(k\)

分析:

对区间进行位运算是没啥公式的,所以要考虑对数的每一位建线段树,记录每一位 \(1\) 出现的次数。询问的时候求出每一位的贡献即可。
线段树维护的是每一位 \(1\) 的出现次数。
区间异或:首先如果 \(k\)\(i\) 位为 \(0\),则异或值不变,否则异或 \(i\) 位的区间 \([l,r]\) 相当于将这个区间的 \(0\) 变成 \(1\)\(1\)变成 \(0\)
区间或:或上某一位1才有意义
区间与:与上某一位0才有意义

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
const int mod = 1e9 + 7;

int n, q, w[N];
int seg[N << 2][19], lazy[N << 2][19];
int opt, res, x, y, k;
LL ans, base;

void build(int rt, int l, int r, int o) {
	if (l == r) {
		seg[rt][o] += ((w[l] >> o) & 1);
		return ;
	}
	int mid = l + r >> 1;
	build(rt << 1, l, mid, o);
	build(rt << 1 | 1, mid + 1, r, o);
	seg[rt][o] = seg[rt << 1][o] + seg[rt << 1 | 1][o];
}

void pushdown(int rt, int l, int r, int mid, int o) {
	if (lazy[rt][o]) {
		lazy[rt << 1][o] ^= 1;
		lazy[rt << 1 | 1][o] ^= 1;
		seg[rt << 1][o] = (mid - l + 1) - seg[rt << 1][o];
		seg[rt << 1 | 1][o] = (r - mid) - seg[rt << 1 | 1][o];
		lazy[rt][o] ^= 1;
	}
}

void update(int rt, int l, int r, int ql, int qr, int o) {
	if (l >= ql && r <= qr) {
		seg[rt][o] = (r - l + 1) - seg[rt][o];
		lazy[rt][o] ^= 1;
		return ; 
	}
	int mid = l + r >> 1;
	pushdown(rt, l, r, mid, o);
	if (ql <= mid) update(rt << 1, l, mid, ql, qr, o);
	if (qr > mid) update(rt << 1 | 1, mid + 1, r, ql, qr, o);
	seg[rt][o] = seg[rt << 1][o] + seg[rt << 1 | 1][o];
}

int query(int rt, int l, int r, int ql, int qr, int o) {
	if (l >= ql && r <= qr) {
		return seg[rt][o];
	}
	int mid = l + r >> 1, ans = 0;
	pushdown(rt, l, r, mid, o);
	if (ql <= mid) ans += query(rt << 1, l, mid, ql, qr, o);
	if (qr > mid) ans += query(rt << 1 | 1, mid + 1, r, ql, qr, o);
	return ans;
}

int main() {
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; i++) scanf("%d", w + i);
	for (int i = 0; i <= 17; i++) build(1, 1, n, i);
	while (q--) {
		scanf("%d", &opt);
		if (opt == 1) {
			scanf("%d %d", &x, &y);
			ans = 0, base = 1;
			for (int i = 0; i <= 17; i++) {
				ans += 1LL * base * query(1, 1, n, x, y, i);
				base <<= 1;
			}
			printf("%lld\n", ans);
		} else {
			scanf("%d %d %d", &x, &y, &k);
			for (int i = 0; i <= 17; i++) {
				res = ((k >> i) & 1);
				if (res) update(1, 1, n, x, y, i);
			}
		}
	}
	return 0;
}
posted @ 2019-10-11 22:07  Chase。  阅读(177)  评论(0编辑  收藏  举报