Codeforces 617E XOR and Favorite Number

本文详细介绍了如何通过离线查询和莫队算法解决一个涉及复杂区间查询与异或运算的问题。文章阐述了核心思路,包括区间预处理、状态转移和时间复杂度优化,提供了一个快速实现的代码示例。适用于需要处理大规模数组和频繁区间查询的场景。

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

http://codeforces.com/problemset/problem/617/E  

题意:长度为n(1e5)的数组,m(1e5)询问和一个目标值k,每个询问需要你回答对于区间【l,r】,其内有多少对i、j(i<=j)满足a[i]^a[i+1]^...^a[j]==k。

思路:可以使用离线查询,故,可以考虑使用莫队算法。使用之前需要一些脑洞来分析这个问题。

要点分析:

1.对于区间【l,r】,其内的a[i]^a[i+1]^...^a[j]=pre[i-1]^pre[j]。

2.如果a^b=c,那么a^c=b,如果pre[i]^pre[j]=k,那么pre[i]=pre[j]^k。

3.对于区间【l,r】,如果此时其答案为temp,且你知道区间【l-1,r】内的每一个pre值的个数cnt,那么对于【l,r+1】,其答案则为temp+cnt[ pre[r^k] ]。即使O(1)时间内实现了区间的一步移动(其他方向的也是如此)

注意事项:下面代码中update()函数需重点理解,cnt和temp更新的先后顺序不同,重点体会。

AC代码(不用cout输出ans会快300ms):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
using namespace std;

typedef long long LL;
const int maxn=(1e5)+5;
const int maxk=(1e6)*3;

int n,m,k,block;
int pre[maxn],a[maxn],cnt[maxk+5];
LL ans[maxn];
LL temp;

struct node{
    int l,r,id,pos;
}q[maxn];

int cmp(const node& a,const node& b){
    if(a.pos!=b.pos) return a.pos<b.pos;
    return a.r<b.r;
}

void update(int cur,int add){
    if(add==1){
        temp+=cnt[cur^k];
        cnt[cur]++;
    }
    else{
        cnt[cur]--;
        temp-=cnt[cur^k];
    }
}

void solve(){
    temp=0;
    if(k==a[1])  temp=1;
    cnt[0]++;
    cnt[a[1]]++;
    int l=1;
    int r=1;
    for(int i=1;i<=m;i++){
        while(l<q[i].l){
            update(pre[l-1],-1);
            l++;
        }
        while(l>q[i].l){
            l--;
            update(pre[l-1],1);
        }
        while(r<q[i].r){
            r++;
            update(pre[r],1);
        }
        while(r>q[i].r){
            update(pre[r],-1);
            r--;
        }
        ans[q[i].id]=temp;
    }
}

int main (){
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        pre[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            pre[i]=pre[i-1]^a[i];
        }
        int block=sqrt(m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
            q[i].pos=q[i].l/block;
        }
        sort(q+1,q+m+1,cmp);
        memset(cnt,0,sizeof(cnt));
        solve();
        for(int i=1;i<=m;i++)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值