题目链接
大致意思就是给你n个数m个操作,然后问每个操作l到r区间内出现偶数次的数的异或和.
从别人的博客里面找到的这个题,很不错。。。
思路就是离线+树状数组。
树状数组维护些什么呢,维护l 到 r区间内每个数的异或和,除此之外我们还要求出一个前缀异或和。
我们知道一个数如果异或偶数次的话那么它就会变为零,所以如果一个数出现了偶数次,那么在l到r这段异或前缀和中这个数就消失了,留下来的就是出现奇数次的异或和,而我们之前用树状数组维护了l 到r的所有数的异或和(并不是所有数例如 2 2 2 2 3 4 5 我们就维护的是 2 3 4 5 每个数出现一次的异或和) 然后用它异或上前缀和就可以得到 偶数次的异或和 因为前缀和留下来的是奇数次的数异或在一起,在异或上每个数那么奇数次的数就会消失例如上面那个例子 我们的前缀和其实是 3^ 4^ 5 而我们用树状数组维护出2 ^ 3 ^ 4 ^ 5两个数再异或一次就得出了偶数次的数的异或值即2
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000000;
int a[1000004];
int sum1[1000004];
int pre[1000004];
void add(int x,int y){
int num = 0;
while(x <= maxn){
sum1[x] ^= y;
x += x&(-x);
}
}
int sum(int x){
int res = 0;
while(x){
res ^= sum1[x];
x -= x&(-x);
}
return res;
}
map<int , int > vis;
struct node{
int x,y;
int id;
}T[maxn];
bool cmp(node a, node b){
return a.y < b.y;
}
int ans[maxn];
int main()
{
ios::sync_with_stdio(false);
int n;
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> a[i];
pre[i] = pre[i - 1]^a[i];
}
int q;
cin >> q;
for(int i = 0; i < q ;i ++){
node &a = T[i];
cin >> a.x >>a.y;
a.id = i;
}
//离线 顾名思义就是将所有问题先存下来 一个一个处理 最后再统一输出
sort(T , T + q ,cmp);
int j = 1;//注意要从1开始 直接从l开始会超时
for(int i = 0; i < q; ){
for(; j <= T[i].y; j ++){
if(vis[a[j]] > 0){//之前出现过的,现在原来的位置上消除掉它对后面的影响
add(vis[a[j]], a[j]);
add(j, a[j]);//再从当前位置向后异或
vis[a[j]] = j;
}
else{
add(j, a[j]);
vis[a[j]] = j;
}
}
ans[T[i].id] = sum(T[i].y)^sum(T[i].x - 1)^pre[T[i].y]^pre[T[i].x- 1];//求出所有答案
i ++;
}
for(int i = 0; i < q; i ++){
cout << ans[i] <<" ";
}
cout << endl;
}