Codeforces 703D

Problem

对于一个由N个数组成的序列,有M次询问。每次问a[l ~ r]这个区间里,出现次数为偶数的数的异或和(没出现的数不算)。

Solution

由于是静态的区间处理,首先想到莫队。但是1e6的数据量,显然N*sqrt(N)就已经1e9了。

然后我和队友展开了激烈的讨论。


我这种位运算的蒟蒻,显然没想到这个性质。感谢我机智强大的队友。于是我就被启发了!!



如图。最后我的思路卡在了如何弄偶数。



emmm,如何去重异或.....真是个问题。


最后,僵持到9点多,我们决定,还是写莫队吧。

最终,不负众望的tle在了第14个点。(我的队友听说t了,就并没有写下去【翻白眼】)

#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
#define N 1000010
using namespace std;
map<int, int>mp;
struct node {int l, r, id;}que[N];
int n, a[N], m, blank, num[N], temp[N], ans1[N];
inline bool mycmp(node x, node y) {
	if(x.l/blank == y.l/blank) return (x.l/blank % 2 == 0)?x.r<y.r:x.r>y.r;
	return x.l < y.l;
}
inline char gc() {
	static char now[1<<16], *S, *T;
	if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
	return *S++;
}
inline int read() {
	int x = 0; char ch = gc();
	while(ch < '0' || ch > '9') ch = gc();
	while(ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = gc();}
	return x;
}
inline void print(int x) {
	if(!x) {putchar('0'); puts(""); return ;}
	int prt[30], now = 0;
	while(x) {prt[++now] = x % 10; x/= 10;}
	for(int i = now; i >= 1; --i) putchar('0' + prt[i]); puts("");
}
int main() {
	n = read(); blank = (int)sqrt(n); if(blank * blank < n) ++blank;
	for(int i = 1; i <= n; ++i) a[i] = read();
	m = read();
	for(int i = 1; i <= m; ++i) {que[i].l = read(); que[i].r = read(); que[i].id = i;}
	sort(que+1, que+m+1, mycmp);
	memcpy(temp, a, sizeof(temp)); sort(temp+1, temp+n+1); int cnt = unique(temp+1, temp+n+1) - temp - 1;
	memset(num, 0, sizeof(num)); for(int i = 1; i <= cnt; ++i) mp[temp[i]] = i;
	int lcur = 1, rcur = 0; int ans = 0;
	for(int i = 1; i <= m; ++i) {
		if(lcur < que[i].l) {
			for(int j = que[i].l - 1; j >= lcur; --j) {
				--num[mp[a[j]]];
				if(num[mp[a[j]]] > 0) ans^= a[j];
			}
			lcur = que[i].l;
		}
		if(lcur > que[i].l) {
			for(int j = lcur - 1; j >= que[i].l; --j) {
				++num[mp[a[j]]];
				if(num[mp[a[j]]] > 1) ans^= a[j];
			}
			lcur = que[i].l;
		}
		if(rcur < que[i].r) {
			for(int j = rcur + 1; j <= que[i].r; ++j) {
				++num[mp[a[j]]];
				if(num[mp[a[j]]] > 1) ans^= a[j];
			}
			rcur = que[i].r;
		}
		if(rcur > que[i].r) {
			for(int j = que[i].r + 1; j <= rcur; ++j) {
				--num[mp[a[j]]];
				if(num[mp[a[j]]] > 0) ans^= a[j];
			}
			rcur = que[i].r;
		}
		ans1[que[i].id] = ans;
	}
	for(int i = 1; i <= m; ++i) print(ans1[i]);
	return 0;
}

带着疑问我就睡着了。

转天早晨一想,我们可以按操作的右端点排序,我们维护a[1~query[i].r]这段的去重异或和就好了。如果我们遇到一个重复的,那就把前面出现的那个位置变成0,然后在新的位置加上这个数。因为你对于以后的询问,全都是覆盖区间更靠右的,那么在更左边出没出现过也没什么分别(更通俗点来说,如果再询问被清零的点所在的区间,就会少异或上这个数。所以我们才使右端点单调递增,避免了这个问题)。这个点操作区间查询,采用树状数组就能解决。那如何记前面有没有出现过呢?我们把数值离散化,然后记last[i]为——映射为i的那个数值上一次出现是在哪个位置,即可解决。

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define N 1000010
using namespace std;
map<int, int>mp;
int n, a[N], sum[N], m, bit[N], last[N], ans[N];
struct node {int l, r, id;}que[N];
inline bool cmp1(node xx, node yy) {return xx.r < yy.r;}
inline char gc() {
	static char now[1<<16], *S, *T;
	if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
	return *S++;
}
inline int read() {
	int x = 0; char ch = gc();
	while(ch < '0' || ch > '9') ch = gc();
	while(ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = gc();}
	return x;
}
inline void print(int x) {
	if(!x) {putchar('0'); puts(""); return ;}
	int prt[30], now = 0;
	while(x) {prt[++now] = x % 10; x/= 10;}
	for(int i = now; i >= 1; --i) putchar('0' + prt[i]); puts("");
}
inline void add(int p, int x) {for(int i = p; i <= n; i+= i & -i) bit[i]^= x;}
inline int query(int p) {
	int ret = 0;
	for(int i = p; i > 0; i-= i & -i) ret^= bit[i];
	return ret;
}
int main() {
	n = read();
	for(int i = 1; i <= n; ++i) {a[i] = read(); if(!mp[a[i]]) mp[a[i]] = i;}
	sum[0] = 0; for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] ^ a[i];
	m = read();
	for(int i = 1; i <= m; ++i) {que[i].l = read(); que[i].r = read(); que[i].id = i;}
	sort(que+1, que+m+1, cmp1);
	memset(bit, 0, sizeof(bit)); int p = 0; memset(last, 0, sizeof(last));
	for(int i = 1; i <= m; ++i) {
		for(int j = p + 1; j <= que[i].r; ++j) {
			if(last[mp[a[j]]]) add(last[mp[a[j]]], a[j]);
			add(j, a[j]);
			last[mp[a[j]]] = j;
		}p = que[i].r;
		ans[que[i].id] = (sum[que[i].r]^sum[que[i].l - 1]) ^ (query(que[i].r) ^ query(que[i].l - 1));
	}
	for(int i = 1; i <= m; ++i) print(ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/richard_for_oi/article/details/79686628