异或序列(莫队)

本博客探讨了如何使用莫队算法解决一个关于异或序列的查询问题。题目要求在给定的整数数列中,计算区间内异或和等于特定值的子序列数量。文章提供了输入输出样例,并分享了算法思路,包括处理异或前缀和以及利用前缀和计数解答问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

6759: 异或序列

时间限制: 1 Sec  内存限制: 128 MB
提交: 191  解决: 88
[提交] [状态] [讨论版] [命题人: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。

 

来源/分类

重庆OI2018 

分析:借这个题讲一下稍微说一下莫队吧,看了几篇博客略有感悟。这个题看明白了就是一道裸莫队板子。

1、首先处理异或前缀和 a。

2、假设i到j的异或和为k,那么有a[i-1]^a[j]=k。同样也有a[j]^k=a[i-1]和a[i-1]^k=a[j]。

3、利用s[i]记录前缀和为i的有几个,那么对于当前扫到的位置x明显答案就是ans+=s[a[x]^k],同时s[a[x]]++。同样如果询问范围不在x里了要减去。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<vector>
#include<stdlib.h>
#include<math.h>
#include<queue>
#include<deque>
#include<ctype.h>
#include<map>
#include<set>
#include<stack>
#include<string>
#define INF 100
#define FAST_IO ios::sync_with_stdio(false)
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MAX=1e5+10;
const int mod=1e9+7;
typedef long long ll;
using namespace std;
#define gcd(a,b) __gcd(a,b)
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
inline ll qpow(ll a,ll b){ll r=1,t=a; while(b){if(b&1)r=(r*t);b>>=1;t=(t*t);}return r;}
inline ll inv1(ll b){return qpow(b,mod-2);}
inline ll exgcd(ll a,ll b,ll &x,ll &y){if(!b){x=1;y=0;return a;}ll r=exgcd(b,a%b,y,x);y-=(a/b)*x;return r;}
inline ll read(){ll x=0,f=1;char c=getchar();for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;for(;isdigit(c);c=getchar()) x=x*10+c-'0';return x*f;}
//freopen( "in.txt" , "r" , stdin );
//freopen( "data.txt" , "w" , stdout );
struct node
{
    int id,l,r;
    int block;
}mo[MAX];
int sum[MAX];
int n,m,k,len,res;
int a[MAX],ans[MAX];
int s[MAX];

int cmp(node a,node b)
{
    if(a.block==b.block)
        return a.r<b.r;
    return a.block<b.block;
}
void add(int x)
{
    res+=s[a[x]^k];
    s[a[x]]++;
}
void del(int x)
{
    res-=s[a[x]^k];
    s[a[x]]--;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        a[i]=a[i]^a[i-1];
    }
    len=sqrt(n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&mo[i].l,&mo[i].r);
        mo[i].l--;
        mo[i].id=i;
        mo[i].block=mo[i].l/len;
    }
    sort(mo+1,mo+1+n,cmp);
    int left=1,right=0;
    for(int i=1;i<=m;i++)
    {
        while(right<mo[i].r) add(++right);///询问范围大于已经扫到的范围。
        while(left>mo[i].l) add(--left);
        while(mo[i].r<right) del(right--);///询问范围小于已经扫到的范围。
        while(mo[i].l>left) del(left++);
        ans[mo[i].id]=res;
    }

    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值