【树状数组】区间出现偶数次数的异或和(区间不同数的异或和)@ codeforce 703 D

【树状数组】区间出现偶数次数的异或和(区间不同数的异或和)@ codeforce 703 D

PROBLEM

题目描述

初始给定n个卡片拍成一排,其中第i个卡片上的数为x[i]。
有q个询问,每次询问给定L和R表示,询问的区间【L,R】内的卡片所有出现了偶数次的数的异或和是多少。

输入

输入一行两个整数n,q。
第二行n个整数,第i个数为x[i]。
接下来q行,每行两个整数L和R,表示询问的区间。

输出

输出q行,其中第i行表示第i次询问的区间出现偶数次的数的异或和。

样例输入

3 1
3 7 8
1 3

样例输出

0

SOlUTION

区间内出现偶数次的数异或和 = 区间内出现奇数次的数的异或和^区间内出现过的数的异或和
区间内出现奇数次的数的异或和 = 区间所有数的异或和
所以,区间内出现偶数次的数异或和 = 区间所有数的异或和^区间内出现过的数的异或和


于是问题转化成求区间内出现过的数的异或和:
可以类比求区间内不同数的个数:树状数组离线做法
这里树状数组sum(i)的含义就是指以当前i为结尾的前缀区间的不同数的异或和
先对询问按右端点排序
然后遍历每个询问,对于当前位置p,如果当前位置上的数x在之前出现过,把它在之前位置上的影响删除:

add(last[x],x);//由异或的性质可以知道,再异或一次就可以消除影响)

把它在当前位置的影响插入:

add(p,x);

更新记录最后位置的数组last[]

CODE

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1000005;

int n,q,x[MAXN];
int xorsum[MAXN],ans[MAXN];
map<int,int> last; // last position (right-most)

struct Query{
    int l,r,ind;
}qry[MAXN];

bool qcmp(Query a,Query b){
    return a.r<b.r;
}

int b[MAXN];

int lowbit(int x){
    return x&-x;
}

void add(int pos,int val){
    while (pos<=n) {
        b[pos] ^= val;
        pos+=lowbit(pos);
    }
}

int sum(int pos){
    int res = 0;
    while (pos>=1){
        res ^= b[pos];
        pos-=lowbit(pos);
    }
    return res;
}

int main() {
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",x+i);
        xorsum[i] = xorsum[i-1]^x[i];
    }
    for(int i=1;i<=q;i++){
        scanf("%d%d",&qry[i].l,&qry[i].r);
        qry[i].ind = i;
    }
    sort(qry+1,qry+q+1,qcmp); //sort query by right endpoint
    for(int i=1,j=1;i<=q;i++){
        while (j<=qry[i].r){
            if(last[x[j]]) add(last[x[j]],x[j]); //delete pos
            last[x[j]] = j;
            add(j,x[j]); //add new pos
            j++;
        }
        ans[qry[i].ind] = sum(qry[i].r)^sum(qry[i].l-1)^xorsum[qry[i].r]^xorsum[qry[i].l-1];
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}
posted @ 2019-01-14 11:38  NeilThang  阅读(350)  评论(0编辑  收藏  举报