题意
给定序列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;
}