先看一道小学数学题: 求
[
l
,
r
]
[l,r]
[l,r]这个区间内
a
i
a_i
ai在子区间中出现的次数,
a
i
a_i
ai不重复。
区间内以
l
l
l为左端点的区间个数有
l
l
l个,我们将这些区间字典序排序,假设我们当前要统计的数的位置为
x
x
x,那么
x
x
x所对应的数字只可能在
x
x
x之前出现,且在每个左端点不同的区间内出现的次数为
r
−
x
+
1
r-x+1
r−x+1,在x之前左端点不同的区间有
x
−
l
+
1
x-l+1
x−l+1个,那么每个数字出现的次数为
(
x
−
l
+
1
)
(
r
−
x
+
1
)
(x-l+1)(r-x+1)
(x−l+1)(r−x+1)
那么我们来考虑下这道题
由于异或的自反性,一个数字能对其他数字产生贡献当且仅当这个数字的出现次数为奇数,根据我们上面推出来的每个数字出现次数的公式,我们可以发现,只有当
x
−
l
,
r
−
x
x-l,r-x
x−l,r−x同时为偶数时才能产生贡献,即
x
,
l
,
r
x,l,r
x,l,r同奇偶。 知道了这个性质,我们在查询区间时只需查询该区间内于
l
,
r
l,r
l,r同奇偶的数就可以了。 快速计算区间异或值可以采取维护前缀和的方法。
c
o
d
e
code
code
#include<bits/stdc++.h>usingnamespace std;constint N =2e5+100;int n, m;int a[N];struct BIT {int c[N];inlineintlowbit(int x){return x &-x;}inlinevoidadd(int x,int k){for(; x <= n; x +=lowbit(x)) c[x]^= k;}inlineintask(int x){int ret =0;for(; x; x -=lowbit(x)) ret ^= c[x];return ret;}} a1, a2;intmain(){scanf("%d%d",&n,&m);for(int i =1; i <= n;++i){scanf("%d",&a[i]);}for(int i =1; i <= n; i +=2) a1.add(i, a[i]);for(int i =2; i <= n; i +=2) a2.add(i, a[i]);for(int i =1; i <= m;++i){int opt, x, y;scanf("%d%d%d",&opt,&x,&y);if(opt ==1){if(x &1) a1.add(x, y ^ a[x]), a[x]= y;else a2.add(x, y ^ a[x]), a[x]= y;}else{if(!((y - x +1)&1))puts("0");else{if(x &1)printf("%d\n", a1.ask(y)^ a1.ask(x -1));elseprintf("%d\n", a2.ask(y)^ a2.ask(x -1));}}}return0;}