Codeforces Round #365 (Div. 2) Problem D.Mishka and Interesting sum 解题报告

题目链接:Here!

题目大意:给你n个数,然后m次查询,每一次查询输出所有在给定区间内出现偶数次数的数的异或值,如果只有一个出现偶数次数的数,则直接输出该数,如果没有出现偶数次数的数,则输出0。

解题思路:看到区间查询,第一反应是线段树,但是用线段树来统计区间内出现次数为偶数次数的数并且保存这些数的异或值比较麻烦。本题的1 ≤ n ≤ 1 000 000,莫队的时间复杂度为O(n^1.5)显然很困难,那么我们应该怎么做呢?首先考虑异或这个位运算的性质a  b  b=a,那我们可以很容易知道,如果一个数被异或偶数次,那么异或结果不变。那么一个区间内所有出现偶数次数的数的异或值就等于这个区间内所有不同数的异或值与所有出现奇数次的数异或的结果。出现奇数次的数的异或值即为这个区间内所有数的异或值,那我们只需要计算出区间内所有不同数的异或值即可。如果直接把每一个区间都遍历一遍的话显然时间复杂度太高,那我们把这些区间按照右端点排序,记录每一个数上一次出现的位置(第一次出现的话就设为它自己所在的位置),然后从1-n扫一遍,把每一个扫到的数加入到树状数组中,如果扫的过程中当前的数的上一次出现的位置不是当前的位置,那么在将树状数组中上一次出现的位置的影响消除,然后在当前位置插入该数。那么,当我们每扫到一个查询的右端点时,我们记录下该查询的右端点和左端点在树状数组中对应的值的异或值,最后再和之前记录过的该区间所有数的异或值异或即是答案。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <set>
#define LL long long
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 1000005 ;
int a[MAXN];//树状数组
int b[MAXN];
int tot[MAXN];//数组的前缀异或值
struct Query{
    int x,y,pos,ans;
}q[MAXN];//查询
int tt[MAXN];//记录答案
map<int,int>Mp;
int last[MAXN];//记录上一个相同的数出现的位置
void add(int i,int x){for(;i<=MAXN;i+=(i&-i))a[i]^=x;}
int Xor(int i){int res=0;for(;i>0;i-=(i&-i))res^=a[i];return res;}
inline bool cmp(const Query& q,const Query& w){if(q.y!=w.y)return q.y<w.y;return q.x<w.x;}

int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        tot[0]=0;
        int cnt=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
            if(!Mp.count(b[i]))//离散化
                Mp[b[i]]=cnt++;
            if(last[Mp[b[i]]]==0)last[Mp[b[i]]]=i;
            tot[i]=tot[i-1]^b[i];
        }
        int m;
        scanf("%d",&m);
        for(int i=0;i<m;i++)
            scanf("%d %d",&q[i].x,&q[i].y),q[i].pos=i;
        sort(q,q+m,cmp);
        int re=0;
        for(int i=1;i<=n&&re<m;i++)
        {
            if(last[Mp[b[i]]]!=i){add(last[Mp[b[i]]],b[i]);last[Mp[b[i]]]=i;}
            add(i,b[i]);
            while(i==q[re].y&&re<m){q[re].ans=Xor(i)^Xor(q[re].x-1);re++;}
        }
        for(int i=0;i<m;i++)
            tt[q[i].pos]=q[i].ans^tot[q[i].x-1]^tot[q[i].y];
        for(int i=0;i<m;i++)
            printf("%d\n",tt[i]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值