[知识学习] 莫队

迟到的莫队知识总结

最近学习了一种说优雅也挺优雅,说暴力也挺暴力的算法:莫队算法

普通莫队

例题

给出一个长度为\(n\)的数列,\(a_1,a_2,...,a_n\),有\(q\)个询问,每个询问给出数对\((i,j)\),需要你给出\(a_i,a_{i+1},...,a_j\)这一段中有多少不同的数字

当然你也可以做HH的项链,题意是一样的,只是这个题目卡了莫队算法

  • 首先考虑一下暴力

对于每个询问,直接从\(l\)到\(r\)扫一遍,统计 复杂度\(O(nq)\) 显然不行(废话

  • 考虑优化

维护两个指针\(l\),\(r\),现在\(l\),\(r\)统计的区间就是\([l,r]\),每次往新的询问区间去靠近,分四种情况:

1.\([l,r]->[l+1,r]\) :统计答案时先减去现在\(l\)位置的答案,再把\(l++\)

2.\([l,r]->[l,r+1]\) :统计答案是先把\(r++\),在加上\(r++\)之后的位置的答案

3.\([l,r]->[l-1,r]\) :统计答案是先把\(l--\),在加上\(l--\)之后的位置的答案

4.\([l,r]->[l,r-1]\) :统计答案时先减去现在\(r\)位置的答案,再把\(r--\)

总结一下规律:区间扩大时就先\(l--\)(\(r++\)),再加上答案,区间缩小时就先减去答案,再\(l++\)(\(r--\))

这样就优化了不少,但对于询问区间一左一右来来回回跳动的情况,又退化成了\(O(nq)\)

  • 进一步优化

因为可以离线计算,所以可以先把询问区间存下来,排个序,按一定顺序进行询问,就可以避免出现一左一右询问的情况

但是按什么来排序?每个区间的左端点顺序?

显然不行吧,万一右端点又是一左一右的乱跳呢?

这时,分块的思想就派上用场了;

我们对询问进行分块,如果两个询问左端点在同一块,就按右端点的块排序,否则就按左端点的块排序

这样就会每个块内指针跳动了,这也就是莫队算法的精髓

贴个代码:

#include <bits/stdc++.h>
#define N (1000000+5)
using namespace std;
typedef long long LL;
int n,m,l,r,bl;
LL ans;
int pos[N],cnt[N],a[N];
LL res[N];
struct node{
    int l,r,id;
}q[N];
bool cmp(node x,node y){//对询问排序
    if(pos[x.l]!=pos[y.l]) return pos[x.l]<pos[y.l];
    return pos[x.r]<pos[y.r];
}
inline void add(int x){if(++cnt[x]==1) ans++;}//加答案
inline void del(int x){if(--cnt[x]==0) ans--;}//减答案
void solve(int l,int r){
    sort(q+1,q+m+1,cmp);
    for(int i=1;i<=m;i++){
        node now=q[i];
        while(l>now.l) add(a[--l]);//四个操作
        while(r<now.r) add(a[++r]);
        while(l<now.l) del(a[l++]);
        while(r>now.r) del(a[r--]);
        res[q[i].id]=ans;
    }
}
int main(){
    scanf("%d",&n);
    bl=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
        pos[i]=(i-1)/bl+1;//分块
    }
    solve(1,0);
    for(int i=1;i<=m;i++){
        printf("%lld\n",res[i]);
    }
    return 0;
}

普通莫队是不是很简单?

洛谷还有一道模板题:小Z的袜子

只不过这个题要推柿子,就交给自己完成,这里就不展示代码了


带修莫队

先咕着,下午再更

(咕咕咕)

原文地址:https://www.cnblogs.com/Xx-queue/p/12205076.html

时间: 2024-08-30 18:30:39

[知识学习] 莫队的相关文章

莫队算法小结以及模板题

被splay折磨了一个星期还是抄模板都wa,调试的时候查数据都发现数据太大不能查的蒟蒻杨澜决定学习莫队算法, 但是 万万没想到 莫队也到处都是坑 ----------------------------------------------题记 莫队这个东西就是一个非常简♂单的类似动规的东西,也是那种不会就一直不会,一看题解秒懂的题,[虽然我看题解也半天看不懂但是只是第一道题,万事开头难嘛(也许吧)] 莫队算法是一种基于分块的离线算法,主要用来解决一些区间的询问[就是暴力],我们需要做的就是把询问

「知识学习&amp;日常训练」莫队算法(一)(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)\)有多少组. 分析 对于这种离线区间的查询问题(不涉及对区间的更改),我们可以使用莫队算法解决.这类问题是什么类型?对于序列上的区间询问

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并不在意两

BZOJ - 3757 树上莫队解决离线路径问题 &amp; 学习心得

题意:给你一棵树,求u,v最短路径的XXX(本题是统计权值种类) 今天课上摸鱼学了一种有意思的处理路径方式(其实是链式块状树翻车了看别的),据说实际运行跑的比XX记者还快 大概就是像序列莫队那样 首先是对暴力查询的优化 第一关键字是块(树上分块),第二关键字是dfs序,这样保证了离线操作的下界最优 其次是转移的优化 我把大佬的话再转述一遍: 设\(S(u,v)\):\(u-v\)最短路径所覆盖的点集 \(S(u,v)=S(root,u)⊕S(root,v)⊕lca(u,v)\) 记\(T(u,v

[bzoj2038]莫队算法学习

解题关键:莫队最重要的是区间之间以$O(1)$的复杂度进行转化,由于电脑原因,后续补上公式推导. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 typedef long long ll; 9 l

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

莫队算法 莫队算法可用于解决一类可离线且在得到区间[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并不在意两只