CF617/E XOR and Favorite Number

题目链接:http://codeforces.com/contest/617/problem/E

题意:给出一个长度为n(1e5)的序列,有m(1e5)次操作,每次操作选择一个L-R区间,然后输出符合条件的i,j的个数:L<i<j<R,ai^a------^aj=k,k是一个给定的值

题解:莫队可做,首先,求一个前缀异或和,如果求ai^……^a[j]的值,那么sum[j]^sum[i-1]。莫队的思想是已知道[L,R]的值,可以在O(1)的时间内算出[L-1,R],[L,R-1]等等的值,

好,现在如果我们维护[L-R]区间,所有{sum[L-1],sum[R-1]}和{sum[L,sum[R]},去除中间重复的部分,然后维护{sum[L-1],sum[R]},这样就方便维护所有的值了。

另外呢,优于是异或,所以呢,中间异或的值很可能会大于1e6,所以标记数组要开的大一点。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn  = (1 << 20) - 1;
int A[maxn], pos[maxn], sz = 320;
LL  num[maxn], Ans[maxn], ans = 0;
struct Edge
{
    int l, r, id;
} E[maxn];
bool cmp (Edge a, Edge b)
{
    if(pos[a.l] == pos[b.l]) return a.r < b.r;
    return pos[a.l] < pos[b.l];
}
int L = 1, R = 0;
int N, M, k;
void add(int x)
{
    ans += num[A[x] ^ k];
    num[A[x]]++;
}
void det(int x)
{
    num[A[x]]--;
    ans -= num[A[x] ^ k];
}
int main ()
{
    scanf("%d %d %d", &N, &M, &k);
    for(int i = 1; i <= N; i++)      
    {
        scanf("%lld", &A[i]);
        A[i] ^= A[i - 1];
        pos[i] = i / sz;
    }
    for(int i = 1; i <= M; i++)
    {
        scanf("%d %d", &E[i].l, &E[i].r);
        E[i].id = i;
    }
    num[0] = 1;
    sort(E + 1, E + 1 + M, cmp);
    for(int i = 1; i <= M; i++)
    {
        while(L < E[i].l)
        {
            det(L - 1);
            L++;
        }
        while(L > E[i].l)
        {
            L--;
            add(L - 1);
        }
        while(R < E[i].r)
        {
            R++;
            add(R);
        }
        while(R > E[i].r)
        {
            det(R);
            R--;
        }
        Ans[E[i].id] = ans;
    }
    for(int i = 1; i <= M; i++) printf("%lld\n", Ans[i]);
    return 0;
}

 

posted @ 2017-06-18 10:41  _Mickey  阅读(326)  评论(0编辑  收藏  举报