异或序列

传送门

题意:n个数,m次查询操作,问你在区间(li,ri)中有多少个子序列的异或和为k

详解:异或为二进制不进位加法,a^b=c,a^c=b,b^c=a;

       首先异或和满足前缀,也就是说设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[sum[r+1]]++,然后求出ans+=cnt[sum[r+1]^k],左边扩展也是如此,不过注意,向左扩展时,对ans的更新是用sum[l-1]的,因为是sum[j]与sum[i-1]可以满足前缀

  而且向右扩展的时候,如果sum[r+1]^k=sum[l-1]的话,ans++,因为我们更新的时候没有计算[l...r+1]区间的影响,所以要维护一下

  而对于区间缩小的情况,就ans先减,再更新cnt,因为要先消除贡献再减cnt,其它步骤类似就好了

#include<bits/stdc++.h>
#include<algorithm>
#include <math.h>
using namespace std;
#define ll long long
typedef pair<ll ,ll>pa;
#define pre(i,x,n) for(int i=x;i<=n;i++)
#define rep(i,n,x) for(int i=n;i>=x;i--)
//priority_queue<ll ,vector<ll>,greater<ll> >q;
ll n,m,a[100010],tem,ans=0,cnt[110010]={0},k,sum[100010];
struct mo
{
    int l,r,id;
}q[100010];
int cmp(mo x,mo y)
{
    if(x.l/tem==y.l/tem)
      return x.r<y.r;
    return x.l<y.l;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    tem=sqrt(n);
    pre(i,1,n){scanf("%lld",&a[i]),a[i]=a[i]^a[i-1];}
    pre(i,1,m)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+1+m,cmp);
    int l=1,r=0;
    pre(i,1,m)
    {
        while(r<q[i].r)
        {
           r++;
           cnt[a[r]]++;
           ans+=cnt[a[r]^k];
           if((a[r]^k)==a[l-1])
             ans++;
        }
        while(r>q[i].r)
        {
            ans-=cnt[a[r]^k];
            cnt[a[r]]--;
            if((a[r]^k)==a[l-1])
              ans--;
            r--;
        }
        while(l<q[i].l)
        {
            ans-=cnt[a[l-1]^k];
            cnt[a[l]]--;
            l++;
        }
        while(l>q[i].l)
        {
            l--;
            cnt[a[l]]++;
            ans+=cnt[a[l-1]^k];
        }
        sum[q[i].id]=ans;
    }
    pre(i,1,m){printf("%lld\n",sum[i]);}
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2020/3/16

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值