异或序列(莫队+异或前缀和)

                                             异或序列

                                                                 时间限制: 1 Sec  内存限制: 128 MB
                                                                             提交: 166  解决: 72
                                                                 [提交] [状态] [讨论版] [命题人:admin]

题目描述

已知一个长度为n的整数数列a1,a2,…,an,给定查询参数l、r,问在al,al+1,…,ar区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y(l≤x≤y≤r),满足ax⊕ax+1⊕⋯⊕ay=k的x,y有多少组。

输入

输入第一行为3个整数n,m,k。第二行为空格分开的n个整数,即a1,a2,…,an。接下来m行,每行两个整数lj,rj,代表一次查询。

输出

输出共m行,对应每个查询的计算结果。

样例输入

4 5 1
1 2 3 1
1 4
1 3
2 3
2 4
4 4

样例输出

4
2
1
2
1

提示

对于30%的数据,1≤n,m≤1000。
对于100%的数据,1≤n,m≤105,0≤k,ai≤105,1≤lj≤rj≤n。

                                                                                       [提交] [状态]

题意 

给出长度为n的序列,给出m个询问,并给出k,每个询问输入l,r。每个询问输出l到r的序列中的所有子串中的异或和为k的子串数量

题解:

莫队

异或,真是个神东西

首先异或和满足前缀,也就是说设sum[i]为a[1]^a[2]^...^a[i],那么a[i]^a[i+1]^...^a[j]=sum[j]^sum[i-1]

而且异或不仅满足交换律,而且对于a^b=c时,a^c=b,b^c=a这两个式子同样成立

那么就好做了,假设当前i到j这个子串的异或和为k,就说明sum[j]^sum[i-1]=k,也就是sum[i-1]^k=sum[j],sum[j]^k=sum[i-1]

然后在区间转移的时候,设cnt[i]为当前区间值为i的前缀有多少个,然后对于增加序列长度的操作,假设新加的位置为r+1,          我们先将cnt[val[r+1]]++,然后求出ans+=cnt[val[r+1]^k],左边扩展也是如此,不过注意,向左扩展时,对ans的更新是用val         [l-1]的,因为是val[j]与sum[i-1]可以满足前缀

PS 感觉这题贼坑,刚开始读完题目就发现可以用莫队,然后想啊想想啊想,想了半天,没想出来要用异或前缀和,有点坑,菜啊。。。。。

  

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <cstring>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <stdlib.h>
#include <list>
#include <map>
#include <utility>
#include <time.h>
#include <set>
#include <bitset>
#include <vector>
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ms(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
ll qpow(ll x, ll y, ll mod){ll s=1;while(y){if(y&1)s=s*x%mod;x=x*x%mod;y>>=1;}return s;}
int n,m,k;
int val[500005];
int cnt[500005];
int ans[500005];
int a[10005];
int Left,Right;
int sum=0;
int block;
struct node
{
   int l,r;
   int id;
}p[100005];
int cmp(node a,node b)
{
    if((a.l/block)==(b.l/block))return a.r<b.r;
    return a.l<b.l;
}
int add(int x)//把x位置的数字加入进来
{
    cnt[val[x]]++;//cnt[i]为当前区间值为i的前缀有多少个
    sum+=cnt[val[x]^k];
}

int remove(int x)//把x位置的数字移出去
{
    cnt[val[x]]--;
    sum-=cnt[val[x]^k];
}

int main()
{

    scanf("%d%d%d",&n,&m,&k);
    block=(int)sqrt(n);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]),val[i]^=val[i-1];
    for(int i=0;i<m;i++)scanf("%d%d",&p[i].l,&p[i].r),p[i].id=i;
    sort(p,p+m,cmp);

    Left=1;
    Right=0;
    cnt[0]=1;
    for(int i=0;i<m;i++)
    {
        while (Left<p[i].l){  remove(Left-1); Left++;}
        while (Left>p[i].l){  Left--; add(Left-1);}
        while (Right<p[i].r){ Right++; add(Right);}
        while (Right>p[i].r){ remove(Right); Right--;}
        ans[p[i].id]=sum;
    }
    for(int i=0;i<m;i++)
        printf("%d\n",ans[i]);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41021816/article/details/81503313