给定序列a,给定 m 个询问\(l,r\),要求每个询问回答\([l,r]\)内出现次数为偶数的数的异或和
发现直接前缀和异或,得到的是出现次数为奇数的数的异或和
于是问题转化成了求出区间内出现过的数的异或和
将询问离线按右端点排序,线段树做扫描线
具体来说就是记一个 last 数组,\(last_i\) 表示i 这个数上一次出现的位置
每次右端点移动,\([last_i+1,i]\)区间内\(a_i\)第一次出现,区间修改,单点查询
#include<bits/stdc++.h> using namespace std; const int N=1e6+11; struct qry_{ int l; int num; }; struct tree{ int l,r; int sum; int lazy; }tre[4*N]; int n,m; int a[N],lsh[N]; int ans[N]; int last[N]; int qzh[N]; vector<qry_> vct[N]; inline int read() { int s=0; char ch=getchar(); while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar(); return s; } void lsh_() { sort(lsh+1,lsh+n+1); int x=unique(lsh+1,lsh+n+1)-lsh; for(int i=1;i<=n;++i) a[i]=lower_bound(lsh+1,lsh+x,a[i])-lsh; return; } void pushdown(int i) { if(tre[i].l==tre[i].r)return; tre[i<<1].lazy^=tre[i].lazy; tre[i<<1|1].lazy^=tre[i].lazy; tre[i<<1].sum^=tre[i].lazy; tre[i<<1|1].sum^=tre[i].lazy; tre[i].lazy=0; return; } void build(int i,int l,int r) { tre[i]=(tree){l,r,0}; if(l==r) return; int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); return; } int query(int i,int x) { if(tre[i].lazy)pushdown(i); if(tre[i].l==tre[i].r) return tre[i].sum; int mid=(tre[i].l+tre[i].r)>>1; if(x<=mid) return query(i<<1,x); else return query(i<<1|1,x); } void insert(int i,int l,int r,int val) { if(tre[i].lazy)pushdown(i); if(tre[i].l>=l&&tre[i].r<=r){tre[i].lazy^=val;tre[i].sum^=val;return;} int mid=(tre[i].l+tre[i].r)>>1; if(l<=mid) insert(i<<1,l,r,val); if(r>mid) insert(i<<1|1,l,r,val); tre[i].sum=tre[i<<1].sum^tre[i<<1|1].sum; return; } int main() { n=read(); for(int i=1;i<=n;++i) a[i]=lsh[i]=read(),qzh[i]=qzh[i-1]^a[i]; lsh_(); m=read(); for(int l,r,i=1;i<=m;++i) { l=read(),r=read(); vct[r].push_back({l,i}); } build(1,1,n); for(int i=1;i<=n;++i) { if(last[a[i]]) insert(1,1,last[a[i]],lsh[a[i]]); insert(1,1,i,lsh[a[i]]); last[a[i]]=i; for(int j=0;j<vct[i].size();++j) ans[vct[i][j].num]=query(1,vct[i][j].l)^qzh[i]^qzh[vct[i][j].l-1]; } for(int i=1;i<=m;++i) printf("%d\n",ans[i]); return 0; }