【区间偶数异或和】【好题】【离线】【树状数组】【前缀和】【前驱思想】

【链接】

http://hznu.club/OJ/problem.php?cid=1227&pid=2

【题意】

求区间出现偶数次的数的异或和

【思路】

首先,没有修改,可以离线查询,减少复杂度。

其次,我们容易知道的是:区间出现奇数次的数的异或和,即为区间异或和。

那么,我们想求的区间出现次数为偶数的数的异或和即为区间中去重后的异或值异或上区间中奇数的异或值。

所以,我们的目标转而去求区间中不同种类的数的异或和。

我们将查询离线,r递增排序。用树状数组维护每个数在数组中只出现一次。

1-n从左往右处理,如果一个数的前驱已经出现,则删去该位置。

将当前数的位置重新加入树状数组中。

即时查询与修改

【代码】

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 1e5 + 6;
ll a[maxn];
ll sum[maxn];
ll ans[maxn];
ll c[maxn];
int last[maxn];
map<ll,int>vis;
int n, m;

struct node {
	int l, r, id;
}Q[maxn];

ll cmp(node a, node b) {
	return a.r < b.r;
}

ll lowbit(ll x) { return x & (-x); }

void update(ll x,ll y) {
	while (x < maxn) {
		c[x] ^= y;
		x += lowbit(x);
	}
}

ll query(ll x) {
	ll res = 0;
	while (x) {
		res ^= c[x];
		x -= lowbit(x);
	}
	return res;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		last[i] = vis[a[i]];
		vis[a[i]] = i;
		sum[i] = sum[i - 1] ^ a[i];
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &Q[i].l, &Q[i].r);
		Q[i].id = i;
	}
	sort(Q + 1, Q + 1 + m, cmp);
	int p = 1;
	for (int i = 1; i <= n; i++) {
		if (last[i])update(last[i], a[i]);
		update(i, a[i]);
		while (p <= m && Q[p].r == i) {
			ll x = query(Q[p].r);
			ll y = query(Q[p].l - 1);
			ans[Q[p].id] = sum[Q[p].r] ^ sum[Q[p].l - 1] ^ x^y;
			p++;
		}
	}
	for (int i = 1; i <= m; i++) {
		printf("%lld\n", ans[i]);
	}
	//scanf("%d", &n);
}

猜你喜欢

转载自blog.csdn.net/running_acmer/article/details/83153003