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