题目大意
给定 n n n 个数的序列 a a a。
m m m 次操作。
操作有一种:
l r
:求 a l ∼ a r a_l\sim a_r al∼ar 中,出现偶数次的数的异或和。
1 ≤ n , m ≤ 1 0 6 1\le n,m\le 10^6 1≤n,m≤106, 1 ≤ a i ≤ 1 0 9 1\le a_i\le 10^9 1≤ai≤109。
解题思路
尝试转换题意,手玩 可知,出现偶数次的数的异或和
=
=
= 出现过数(出现多次算一个)的异或和
xor
\operatorname{xor}
xor 所有数的异或和。
∵
x
xor
x
=
0
∴
所
有
数
的
异
或
和
中
=
出
现
偶
数
次
的
数
的
异
或
和
xor
出
现
奇
数
次
的
数
的
异
或
和
。
∴
出
现
偶
数
次
的
数
的
异
或
和
=
出
现
过
数
(
出
现
多
次
算
一
个
)
的
异
或
和
xor
所
有
数
的
异
或
和
。
\because x \operatorname{xor} x=0\\ \therefore 所有数的异或和中 = 出现偶数次的数的异或和 \operatorname{xor} 出现奇数次的数的异或和。\\ \therefore 出现偶数次的数的异或和 = 出现过数(出现多次算一个)的异或和 \operatorname{xor} 所有数的异或和。
∵xxorx=0∴所有数的异或和中=出现偶数次的数的异或和xor出现奇数次的数的异或和。∴出现偶数次的数的异或和=出现过数(出现多次算一个)的异或和xor所有数的异或和。
所有数异或和可以用前缀和维护。
出现过数(出现多次算一个)的异或和可以线性记录每一个数它之前出现过的位置 p r e pre pre。
若他是第一个出现的数,他之前出现过的位置为 0 0 0。
然后用树状数组维护即可。
即每次将这个点 x x x 加上 a [ x ] a[x] a[x],并将 p r e [ x ] pre[x] pre[x] 也加上 a [ x ] a[x] a[x]。
那么若一个数在这段区间里出现过,那么在树状数组这段区间中他只 xor \operatorname{xor} xor 过奇数次。
询问可以离线考虑,按右端点排序,可以达到 O ( n log n ) \mathcal{O}(n \log n) O(nlogn) 的时间复杂度。
具体参考代码。
CODE
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
inline void write(int x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
const int _ = 1000007;
int c[_];
int n, m;
int a[_];
int sum[_];
int pre[_];
map<int, int> d;
int ans[_];
inline int lowbit(int x)
{
return x & -x;
}
inline void update(int x, int val)
{
if(x == 0) return;
for(int i = x; i <= n; i += lowbit(i))
c[i] ^= val;
}
inline int query(int x)
{
if(x == 0) return 0;
int res = 0;
for(int i = x; i; i -= lowbit(i)) res ^= c[i];
return res;
}
struct abc
{
int l, r, id;
} q[_];
bool cmp(abc a, abc b)
{
return a.r < b.r;
}
signed main()
{
n = read();
for(int i = 1; i <= n; ++i)
{
a[i] = read();
sum[i] = sum[i - 1] ^ a[i];
pre[i] = d[a[i]];
d[a[i]] = i;
}
m = read();
for(int i = 1; i <= m; ++i)
{
q[i].l = read();
q[i].r = read();
q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int tot = 0;
for(int i = 1; i <= m; i++)
{
while(tot < q[i].r)
{
tot++;
update(tot, a[tot]);
update(pre[tot], a[pre[tot]]);
}
ans[q[i].id] = query(q[i].r) ^ query(q[i].l - 1) ^ sum[q[i].r] ^ sum[q[i].l - 1];
}
for(int i = 1; i <= m; ++i)
cout << ans[i] << "\n";
return 0;
}