CodeForces 703D Mishka and Interesting sum
线段树,区间异或性质
题意
给一个长度为n的序列,m次询问。询问一段区间中,出现了偶数次的数的异或和。
思路
首先异或的性质: a xor a=0 。即区间中出现偶数次的数对异或值没有贡献。然后 a xor b xor a=b 。所以求偶数次数的异或值转化为求区间出现过数的异或以及出现奇数次数的异或。
奇数次的异或很好求,预处理前缀异或和即可。因为上面的性质1,偶数次数异或没了。
求出现过的数的异或,我们要保证每个数只计算一次。所以用线段树离线搞,按询问右端点排序。每个数只在树中保留最后一次出现位置即可。然后线段树维护区间异或和。
%%%
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1000007;
const int oo=0x3f3f3f3f;
int num[MAXN];
int odd[MAXN];
struct Query
{
int x, y;
int i, res;
void rd(int _i) { scanf("%d%d", &x, &y);i=_i, res=0; }
}q[MAXN];
bool cmp1(Query a, Query b) { return a.y==b.y ? a.x<b.x : a.y<b.y; }
bool cmp2(Query a, Query b) { return a.i<b.i; }
int stree[MAXN<<2];
void pushup(int rt) { stree[rt]=stree[rt<<1]^stree[rt<<1|1]; }
void build() { M(stree, 0); }
void update(int pos, int v, int l, int r, int rt)
{
if(l==r) { stree[rt]=v;return; }
int mid=(l+r)>>1;
if(pos<=mid) update(pos, v, lson);
else update(pos, v, rson);
pushup(rt);
}
int query(int L, int R, int l, int r, int rt)
{
if(L<=l&&r<=R) return stree[rt];
int mid=(l+r)>>1;
int res=0;
if(L<=mid) res^=query(L, R, lson);
if(R>mid) res^=query(L, R, rson);
return res;
}
map<int, int> la;
int main()
{
int n;scanf("%d", &n);
odd[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d", &num[i]);
odd[i]=num[i]^odd[i-1];
}
int m;scanf("%d", &m);
for(int i=1;i<=m;i++)
q[i].rd(i);
sort(q+1, q+m+1, cmp1);
int curr=q[1].y, curq=1, curnum=0;
while(curq<=m)
{
curr=q[curq].y;
while(curnum<curr)
{
curnum++;
if(la.count(num[curnum]))
update(la[num[curnum]], 0, 1, n, 1);
update(curnum, num[curnum], 1, n, 1);
la[num[curnum]]=curnum;
}
while(curq<=m&&q[curq].y==curr)
{
int x=q[curq].x, y=q[curq].y;
q[curq].res=query(x, y, 1, n, 1)^odd[x-1]^odd[y];
curq++;
}
}
sort(q+1, q+m+1, cmp2);
for(int i=1;i<=m;i++) printf("%d\n", q[i].res);
//system("pause");
return 0;
}