【29.82%】【codeforces 703D】Mishka and Interesting sum

这里写图片描述
这里写图片描述
这里写图片描述
【题解】

题意:
给n个数字组成有序数列;
给m个询问.
对于每个询问区间。输出这个区间里面出现次数为偶数次的所有数的异或值;
做法:
我们可以先求出这段区间里面所有(包括重复的数字)数字的异或值p1;
(出现次数为偶数的数x,这偶数个x的异或值为0(x^x==0),出现次数为奇数的数y(y^y^y==y),这奇数个数y的异或值为y,考虑到这些数字的和,那么剩下的其实就是出现次数为奇数次的所有数的异或值,也即p1);
然后我们再求出这段区间内不同的数字的异或值(去重)p2;
那么我们所需要的答案就是p1^p2;
因为异或了p2,所以p1中出现奇数次的数变成了偶数次->0,而原来出现偶数次的数变成出现奇数次->它本身);
则为我们所需的答案;
自己再想想吧.
然后就是求一段区间内不同的数字的总异或值的问题了。
这个问题可以用树状数组解决;
但是需要离线做;
把每个询问按照右端点升序排;
用map来判重;
枚举所有的数字;
到了一个新的数字;
就判断之前有没有出现过;
如果有出现过就把它之前的位置在树状数组中的相关值异或掉(相当于去除)。
然后给这个值设定一个新的位置(即当前的枚举到的位置);然后修改树状数组的相关位置.
然后如果有右端点和当前处理的数的位置相同。则用树状数组累加前缀异或值(不同的数).然后用一开始处理出的有重复数的前缀异或和和相应的东西异或就可以了。
要注意这道题里面异或有时就相当于减法.有时又相当于加法;

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

const int MAXN = 1e6 + 100;
const int MAXM = 1e6 + 100;

struct abc
{
    int l, r, id;
};

int n, m;
int a[MAXN], c[MAXN], pre[MAXN];
int ans[MAXM];
abc Q[MAXM];
map <int, int> frequent;

bool cmp(abc a, abc b)
{
    return a.r < b.r;
}

void input(int &r)
{
    r = 0;
    char t = getchar();
    while (!isdigit(t)) t = getchar();
    while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
}

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

void add(int pos, int what)//修改某个点,修改相关值
{
    int x = pos;
    while (x <= n)
    {
        c[x] ^= what;
        x += lowbit(x);
    }
}

int sum(int pos)//累加前缀和
{
    int x = pos;
    int now = 0;
    while (x > 0)
    {
        now = now^c[x];
        x -= lowbit(x);//一直往左走
    }
    return now;
}

int main()
{
    input(n);
    pre[0] = 0;
    for (int i = 1; i <= n; i++)
        input(a[i]), pre[i] = pre[i - 1] ^ a[i];
    input(m);
    for (int i = 1; i <= m; i++)
    {
        input(Q[i].l); input(Q[i].r);
        Q[i].id = i;
    }
    sort(Q + 1, Q + 1 + m, cmp);//询问按照右端点升序排序
    int now = 1;//now是当前处理到的询问
    for (int i = 1; i <= n; i++)
    {
        if (frequent[a[i]])//如果之前出现过
            add(frequent[a[i]], a[i]);//将之前的数字剔除
        frequent[a[i]] = i;//然后更新这个数字的新位置;
        add(frequent[a[i]], a[i]);//修改树状数组的相关点
        while (now <= m && i == Q[now].r)//保证右端点之前记录的都是不同的数字的信息
        {//用树状数组的前缀和获取这段区间内的不同数字的异或值
            ans[Q[now].id] = sum(Q[now].r) ^ sum(Q[now].l - 1) ^ pre[Q[now].r] ^ pre[Q[now].l - 1];
            now++;
        }
    }
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}
posted @ 2017-10-06 19:22  AWCXV  阅读(107)  评论(0编辑  收藏  举报