赞
踩
题意:给定你一个长度为n的序列,然后m个询问,每次询问给出L和R,问你在这个区间内出现次数为偶数次的数(每个数只取一个),他们的异或和为多少。
思路:出现次数为偶数次这些异或都会变成0,所以我们直接求区间异或和,求出来的出现次数为奇数词的数的异或和,假如我们能求得这个区间内不同的数的异或和,那他们二者异或一下就可以得到我们想要的答案了。
然后问题就变成了怎么求区间内不同数的异或和呢。m个询问,我们可以离线处理,按照右端点从小到大的顺序排序,然后每次只去更新距离当前右端点r最近的值的位置,再把这个位置之间的位置的值置为0,这样每次区间内我们算的就是一个唯一存在的值,又因为离线处理,按照右端点扫一遍,复杂度是可以的。
这样我们就用一个树状数组维护这个特殊的区间异或和即可。
代码:
#include <bits/stdc++.h> using namespace std; const int MAXN = 1e6 + 7; //对于这种m个询问的题目 可以多想一下 能不能离线搞 这个题就是离线之后 按右端点找就可 int pre[MAXN];//当前数的上一次出现的位置 map<int,int>mp;//记录一下每个数出现的位置 int sxor[MAXN],n,m,ans[MAXN],a[MAXN],c[MAXN]; struct Q{//存询问 int l,r,id; }q[MAXN]; bool cmp(Q a,Q b){ return a.r < b.r; } bool ccmp(Q a,Q b){ return a.id < b.id; } int lowbit(int x){ return x & (-x); } void update(int p,int v){ while(p <= n){ c[p] ^= v;p += lowbit(p); } } int query(int p){ int ans = 0; while(p){ ans ^= c[p];p -= lowbit(p); } return ans; } int main(){ scanf("%d",&n); for(int i = 1;i <= n;i ++){ scanf("%d",&a[i]); pre[i] = mp[a[i]];//存一下当前位置的值上一次出现的位置是哪里 mp[a[i]] = i; } // for(int i = 1;i <= n;i ++) cout<<pre[i]<<' '; cout<<'\n'; for(int i = 1;i <= n;i ++) sxor[i] = sxor[i-1] ^ a[i]; scanf("%d",&m); 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 L,R = 1; for(int i = 1;i <= m;i ++){ while(R <= q[i].r){ if(pre[R]){//前面出现过的话 我就把它置为0 也就是再异或一下 update(pre[R],a[R]); } update(R,a[R]); R++; } L = q[i].l; int t = sxor[R-1] ^ sxor[L-1]; // cout<<R-1<<' '<<L<<endl; // cout<<query(R-1)<<"***"<<query(L-1)<<endl; ans[q[i].id] = t ^ query(R-1) ^ query(L-1); } sort(q + 1,q + 1 + m,ccmp); for(int i = 1;i <= m;i ++) printf("%d\n",ans[i]); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。