[upcoj6759]异或序列 莫队

本文深入解析莫队算法在处理区间查询问题上的应用,通过一个具体的编程竞赛题目,展示了如何利用离线查询和异或前缀和技巧,有效地解决大规模数据集上的区间异或和查询问题。适用于对算法竞赛和数据结构感兴趣的读者。

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

时间限制: 1 Sec 内存限制: 128 MB

题目描述

已知一个长度为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≤10^5,0≤k,ai≤10^5,1≤lj≤rj≤n。

来源/分类

重庆OI2018

Solution

讲m种询问离线下来,分区,x=sqrt(m),按照分区标号从小到大,同一小区间r从小到大排序;
借助异或前缀和,sum[i]=sum[i-1]^a[i],之后有a[l]^a[l+1]^…^a[r]=sum[r]^sum[l-1];
此处[curL,curR]是闭区间。
——鄙人手中的第二个莫队代码

#include<bits/stdc++.h>

#define ll long long
#define L(u) u<<1
#define R(u) u<<1|1

using namespace std;
const int MX = 100001;
int n, m, k;
int a[MX], s[MX<<1];
int x, curL, curR, res;
int pos[MX];
ll ans[MX];

struct data {
    int L, R, id;

    bool operator<(const data &b) const {
        if (pos[L] == pos[b.L]) return R < b.R;
        return L < b.L;
    }
} q[MX];

inline void add(int p) {
    s[a[p]]++;
    res += s[a[p]^k];
}
inline void del(int p) {
    res -= s[a[p]^k];
    s[a[p]]--;
}
inline void addL(int l, int r) {}

inline void delL(int l, int r) {}

inline void addR(int l, int r) {}

inline void delR(int l, int r) {}


int main() {
    //freopen("../in", "r", stdin);
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        a[i] = a[i - 1] ^ a[i];
    }

    x = sqrt(n);
    for (int i = 0; i < m; ++i) {
        q[i].id = i;
        pos[i] = i / x;
        scanf("%d%d", &q[i].L, &q[i].R);
        q[i].L--;
    }

    sort(q, q + m);
    res = 0, curL = 0, curR = -1;
    for (int i = 0; i < m; ++i) {
        while (curL < q[i].L) del(curL++);
        while (curL > q[i].L) add(--curL);
        while (curR < q[i].R) add(++curR);
        while (curR > q[i].R) del(curR--);
        ans[q[i].id] = res;
    }
    for (int i = 0; i < m; ++i)
        printf("%d\n", ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值