1. 程式人生 > >【區間偶數異或和】【好題】【離線】【樹狀陣列】【字首和】【前驅思想】

【區間偶數異或和】【好題】【離線】【樹狀陣列】【字首和】【前驅思想】

【連結】

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