「知识学习&日常训练」莫队算法(一)(Codeforce Round #340 Div.2 E)

题意

已知一个长度为\(n\)的整数数列\(a[1],a[2],…,a[n]\),给定查询参数\(l,r\),问\([l,r]\)内,有多少连续子段满足异或和等于\(k\)。

也就是说,对于所有的\(x,y (l\le x\le y\le r)\),能够满足\(a[x]\oplus a[x+1]\oplus ...\oplus a[y]=k\)的\((x,y)\)有多少组。

分析

对于这种离线区间的查询问题(不涉及对区间的更改),我们可以使用莫队算法解决。这类问题是什么类型?对于序列上的区间询问问题,如果从\([l, r]\)的答案能够\(O(1)\)扩展到\([l+1,r],[l,r?1],[l - 1, r],[l, r + 1]\)的答案,那么可以在\(O(n\sqrt n)\)的复杂度内求出所有询问的答案。

这题为什么可以?因为对于\(x\)至\(y\)的区间异或和,我们可以用前缀异或和的\(x-1\)与\(y\)相异或来解决。

接下来讲讲具体的实现:

(参考:https://blog.sengxian.com/algorithms/mo-s-algorithm

实现:离线后排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间答案。

排序方法:设定块的长度为\(S\),按照\((\lfloor\frac l S\rfloor, r)\)二元组从小到大排序。

复杂度分析:设序列长度为\(n\),询问个数为\(m\)。可以发现从\((l_1, r_1)\)转移到\((l_2, r_2)\)的代价为他们之间的曼哈顿距离。对于每一个询问序列中的每一个块(第一关键字相同),整个块内纵坐标最多变化\(n\)长度(纵坐标必然单调不减),对于每个询问,横坐标最多变化\(S\)。一共有\(\frac n S\)个块,相邻块之间转移的复杂度为\(O(n)\),所以复杂度为\(O(\frac {n^2} S + mS + \frac {n^2} S)\),不妨让\(n, m\)同阶,取\(S = \sqrt n\)??时可达到最优复杂度\(O(n\sqrt n)\)?。

这题的具体实现:我们记\(mp[x]\)为异或和为x的个数。转移区间的时候(不失一般性,考虑区间纯右移),每增加一个点\(r\),这个点对于答案的贡献是\(mp[x\oplus a[r]]\)(异或的性质),同时,它增加了\(mp[a[r]]\)的个数。每减少一个点同理。

代码

参考:https://blog.csdn.net/swust_lian/article/details/50615109

/*
 * Filename: cfr340d2e.cpp
 * Date: 2018-11-09
 */

#include <bits/stdc++.h>

#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()

#define QUICKIO                      ios::sync_with_stdio(false);     cin.tie(0);                      cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)

using namespace std;
using pi=pair<int,int>;
using repType=int;
using ll=long long;
using ld=long double;
using ull=unsigned long long;

const int MAXN=100005;
const int BLOCK=400;

struct Node
{
    ll l,r,id;
    Node(ll _l=0, ll _r=0, ll _id=0):
        l(_l), r(_r), id(_id) {}
    bool operator < (const Node& rhs) const
    {
        if(l/BLOCK!=rhs.l/BLOCK) return l/BLOCK<rhs.l/BLOCK;
        else return r<rhs.r;
    }
};
vector<Node> vec;

ll s[MAXN];
ll ans[MAXN], mp[MAXN*200];
ll n,m,k;
int
main()
{
    scanf("%lld%lld%lld", &n, &m, &k);
    s[0]=0;
    rep(i,1,n)
    {
        ll x; scanf("%lld", &x);
        s[i]=s[i-1]^x;
    }
    rep(i,1,m)
    {
        ll l,r;
        scanf("%lld%lld", &l, &r);
        vec.PB(l-1,r,i); // why l-1: xor(a[x]~a[y])=k <-> s[x-1]^s[y]=k
    }
    sort(ALL(vec));
    ZERO(mp);
    ZERO(ans);
    ll tmp=0;
    int l=vec[0].l, r=vec[0].r;
    rep(i,l,r)
    {
        tmp+=mp[s[i]^k];
        mp[s[i]]++;
    }

    ans[vec[0].id]=tmp;
    rep(i,1,m-1)
    {
        int L=vec[i].l,
            R=vec[i].r;
        while(l>L)
        {
            l--;
            tmp+=mp[s[l]^k];
            mp[s[l]]++; // mp: cnt of xor_sum = s[l]
        }
        while(l<L)
        {
            mp[s[l]]--;
            tmp-=mp[s[l]^k];
            l++;
        }
        while(r<R)
        {
            r++;
            tmp+=mp[s[r]^k];
            mp[s[r]]++;
        }
        while(r>R)
        {
            mp[s[r]]--;
            tmp-=mp[s[r]^k];
            r--;
        }
        ans[vec[i].id]=tmp;
    }
    rep(i,1,m) printf("%lld\n", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/samhx/p/CFR340D2E_Mo-s-Algorithm_a.html

时间: 2024-08-19 03:34:55

「知识学习&日常训练」莫队算法(一)(Codeforce Round #340 Div.2 E)的相关文章

「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)

题意与分析(CodeForces 540D) 代码 #include <iomanip> #include <iostream> #include <cstring> #include <algorithm> #include <vector> #define MP make_pair #define PB push_back #define fi first #define se second #define ZERO(x) memset((x

「日常训练」Paths and Trees(Codeforces Round 301 Div.2 E)

题意与分析 代码 #include <bits/stdc++.h> #define MP make_pair #define PB emplace_back #define fi first #define se second #define ZERO(x) memset((x), 0, sizeof(x)) #define ALL(x) (x).begin(),(x).end() #define rep(i, a, b) for (repType i = (a); i <= (b);

「日常训练」Brackets in Implications(Codeforces Round 306 Div.2 E)

题意与分析 稍微复杂一些的思维题.反正这场全是思维题,就一道暴力水题(B). 代码 #include <bits/stdc++.h> #define MP make_pair #define PB emplace_back #define fi first #define se second #define ZERO(x) memset((x), 0, sizeof(x)) #define ALL(x) (x).begin(),(x).end() #define rep(i, a, b) fo

专题训练之莫队算法

推荐博客/专栏:https://blog.csdn.net/xianhaoming/article/details/52201761莫队算法讲解(含树上莫队) https://blog.csdn.net/hzj1054689699/article/details/51866615莫队算法 https://zhuanlan.zhihu.com/p/25017840莫队算法 例题及讲解:(BZOJ2038)https://www.luogu.org/problemnew/show/P1494 讲解:

BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&amp;&amp;学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 9894  Solved: 4561[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

莫队算法---基础知识介绍(转载)

莫队算法 莫队算法可用于解决一类可离线且在得到区间[l,r][l,r]的答案后,能在O(1)O(1)或O(log2n)O(log2?n)得到区间[l,r+1][l,r+1]或[l−1,r][l−1,r]的答案的问题 先看这样一个问题: 给出n个数字,m次询问,每次询问在区间[li,ri][li,ri]之间任选两个数字相等的概率是多少.(n,q<=50000)(小z的袜子) 在区间[l,r][l,r]中,这个概率是: ∑vi=1C(2,f(i))C(2,r−l+1)∑i=1vC(2,f(i))C(

莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】

很久以前傻乎乎地看来源奇怪的资料的时候被各种曼哈顿弄晕了. 然后现在学会的是分块方法.另新创一个分块方法. 让我们考虑这样一个区间询问问题…… 它有如下的性质: 0,n个数,Q个询问. 1,它没有修改操作,这意味着我们可以按我们喜欢的次序跟询问玩耍.实际上后面会讲到我们完全可以按任意次序玩耍. 2,如果我们知道区间询问 [L , R] 对应的值,我们可以轻易求出 [L±1 , R] 和 [L , R±1] 的值. (其实如果限制增加,比如只能求 [L+1 , R] 和 [L , R-1] 的值,

带修改的莫队算法学习小记

简介 莫涛大神创造出的离线询问算法的带修改版. 算法基础:需要掌握莫队算法,会打暴搜(暴力). 一个叫莫的双端队列. 只支持单点修改 操作方法 普通的不带修改的莫队算法要把每个询问带上两个关键字排序,现在待修改的莫队算法要带上三个关键字排序. 初始操作 fo(i,1,m) { scanf("%s%d%d",s,&k,&l); if (s[0]=='Q')a[++tot].l=k,a[tot].r=l,a[tot].x=num,a[tot].p=tot; else d[+

BZOJ 2038: [2009国家集训队]小Z的袜子(hose) [莫队算法]【学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7687  Solved: 3516[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只