[bzoj5301][莫队算法]异或序列

Description

已知一个长度为 n 的整数数列 a[1],a[2],…,a[n] ,给定查询参数 l、r ,问在 [l,r] 区间内,有多少连续子
序列满足异或和等于 k 。 也就是说,对于所有的 x,y
(l≤x≤y≤r),能够满足a[x]^a[x+1]^…^a[y]=k的x,y有多少组。

Input

输入文件第一行,为3个整数n,m,k。 第二行为空格分开的n个整数,即ai,a2,….an。
接下来m行,每行两个整数lj,rj,表示一次查询。 1≤n,m≤105,O≤k,ai≤105,1≤lj≤rj≤n

Output

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

Sample Input

4 5 1

1 2 3 1

1 4

1 3

2 3

2 4

4 4

Sample Output

4

2

1

2
1

题解

先搞一个前缀异或和
我们考虑已知区间(l,r),加入r+1时对答案的影响
设col[i]表示当前区间内前缀异或和为i的数量,那么加入r+1时答案会增加col[sum[r+1]^K]。注意我们这里没有计算l的影响,即没有计算l~r+1这一段的影响,所以还需要讨论一下sum[r+1]^K是否等于sum[l-1],是的话答案还要增加1
加入或减少l,r同理
容易看出转移是O(1)的,所以上莫队即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
struct ask
{
    int l,r,op;
}a[110000];int n,m,K;
int pos[110000],block;
bool cmp(ask n1,ask n2){return pos[n1.l]!=pos[n2.l]?pos[n1.l]<pos[n2.l]:n1.r<n2.r;}
int sum[110000];
LL col[110000],ans,answer[110000];
int l,r;
void addl(int now){col[sum[now]]++;ans+=col[sum[now-1]^K];}
void dell(int now){ans-=col[sum[now-1]^K];col[sum[now]]--;}
void addr(int now)
{
    col[sum[now]]++;ans+=col[sum[now]^K];
    if((sum[now]^K)==sum[l-1])ans++;
}
void delr(int now)
{
    ans-=col[sum[now]^K];col[sum[now]]--;
    if((sum[now]^K)==sum[l-1])ans--;
}
int main()
{
    scanf("%d%d%d",&n,&m,&K);block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        int x;scanf("%d",&x);
        sum[i]=sum[i-1]^x;
        pos[i]=(i-1)/block+1;
    }
    for(int i=1;i<=m;i++)scanf("%d%d",&a[i].l,&a[i].r),a[i].op=i;
    sort(a+1,a+1+m,cmp);
    ans=0;
    for(int i=a[1].l;i<=a[1].r;i++)
    {
        col[sum[i]]++;
        ans+=col[sum[i]^K];
        if((sum[i]^K)==sum[a[1].l-1])ans++;
    }
    answer[a[1].op]=ans;
    l=a[1].l,r=a[1].r;
    for(int i=2;i<=m;i++)
    {
        while(l<a[i].l)dell(l++);
        while(l>a[i].l)addl(--l);
        while(r<a[i].r)addr(++r);
        while(r>a[i].r)delr(r--);
        answer[a[i].op]=ans;
    }
    for(int i=1;i<=m;i++)printf("%lld\n",answer[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值