CodeForces 703D Mishka and Interesting sum

29 篇文章 0 订阅
10 篇文章 0 订阅

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值