莫队算法

莫队算法·初探总结

爷,独闯天下 提交于 2020-03-26 19:09:36
莫队算法分那么几类: 普通序列 带修改 树上 回滚 支持在线 其实上述的类型还可以组合起来( 非常的毒瘤 )。 个人理解莫队算法的精髓在于如何利用暴力将答案再合理的时间和空间内跑出来。说白了: \[莫队算法=一种很牛逼的自定义排序+分块处理+暴力 \] 首先要理解自定义排序,这个排序之后整个序列可以最快地处理所有的询问(这里暂时不谈第五类问题(支持在线),这里认为莫队是只能离线处理问题的,必须先把所有的问题都离线下来)。怎么为之快,快要看左端点移动的总距离+右端点移动的总距离最小。那么一般用块的奇偶性来排序就够快了。如下: bool cmp(const node &a,const node &b) { return (p[a.l]^p[b.l])?(p[a.l]<p[b.l]):((p[a.l]&1)?(a.r<b.r):(a.r>b.r)); } 排完序后大致移动的次数就在可控的范围内了。 其次是分块,因为一般这种序列题的复杂度是 \(O(\frac{n^2}{S}+mS)\) ( \(n\) 是序列长度, \(m\) 是询问次数, \(S\) 是块大小)。 那么用均值不等式去分析这个复杂度就好了。一般 \(n\) , \(m\) 同阶的时候取 \(\sqrt n\) 最优,但是假如 \(n\) 和 \(m\) 有别的情况的话具体分析,有时候还要决定用时间换空间来保证空间也过得去

模板 - 数据结构 - 莫队算法 / Mo's Algorithm

老子叫甜甜 提交于 2020-03-14 09:11:48
参考一下czq的博客: https://www.cnblogs.com/JHSeng/p/10862295.html (不带修的)莫队算法适用于什么问题?当然是不带修改的区间查询。莫队算法通过离线所有查询,然后对查询进行分块,在查询与查询之间暴力转移。 假如可以通过维护区间端点的信息,从 \([l,r]\) 的状态 \(O(1)\) 转移到 \([l \pm 1,r]\) 以及 \([l,r \pm 1]\) ,则可以从 \([l_1,r_1]\) 的状态 \(O(|l_1-l_2|+|r_1-r_2|)\) 转移到 \([l_2,r_2]\) ,莫队算法就是离线所有的这些查询,然后根据某种次序去回答这些查询,使得转移的距离比较少。注意到假如把 \(l\) 看做横坐标, \(r\) 看做纵坐标,那么查询就是二维平面上的点,总的转移距离最少的就是曼哈顿距离最小生成树。 据说在莫队的论文中指出了在最坏情况下,使用曼哈顿距离最小生成树解决这个问题的复杂度和分块是一样的,只是常数比较小,那么就不需要这么麻烦了。 分块的思路是:按每 \(\sqrt n\) 大小分为一块,左端点不在同一块中的,按左端点的从小到大排序,左端点在同一块中的,按右端点从小到大排序。比如一组 \([1,9]\) 上的查询 \([1,9],[2,4],[2,5],[3,3],[3,7],[4,5],[5,7],[6,8

莫队算法

China☆狼群 提交于 2020-02-06 02:39:30
今天学习了莫队算法,精髓就是处理区间问题。 重点是分块,和指针移动。 /* 学习莫队算法,重点是分块和区间维护 */ /*例如题目,区间内查询数字num出现次数 */ #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int maxn = 50000 + 100; int n;// input n number to arr int num;//查询的数字 int arr[maxn]; int ans[maxn]; int sum; int block; struct node { int left, right; int pos; }query[maxn]; bool cmp(const node &a,const node &b){ if(a.left / block == b.left / block) //同一块的时候 return a.right < b.right; //不同块的时候 return a.left < b.left; } void add(int index){ if(arr[index] == num) sum++; } void sub(int index){ if(arr[index] == num) sum--; } int

莫队算法良心讲解

对着背影说爱祢 提交于 2020-01-12 05:13:57
问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些。   朴素的解法需要读取O(nm)次数。如果数据范围小,可以用数组,时间复杂度为O(nm)。如果使用STL的Map来保存出现的次数,则需要O(nmlogn)的复杂度。有没有更快的方法呢?   注意到询问并没有强制在线,因此我们可以使用离线方法。注意到一点,如果我们有计算完[L, R]时的“中间变量”(在本题为每个数出现的次数),那么[L - 1, R]、[L + 1, R]、[L, R - 1]、[L, R + 1]都能够在“中间变量”的“基本操作时间复杂度” (1) 得出。如果能安排适当的询问顺序,使得每次询问都能用上上次运行产生的中间变量,那么我们将可以在更优的复杂度完成整个询问。 (1) 如果数据较小,用数组,时间复杂度为O(1);如果数据较大,可以考虑用离散化或map,时间复杂度为O(logn)。   那如何安排询问呢?这里有个时间复杂度非常优秀的方法:首先将每个询问视为一个“点”,两个点P1, P2之间的距离为abs(L1 - L2) + abs(R1 - R2),即曼哈顿距离,然后求这些点的最小生成树,然后沿着树边遍历一次。由于这里的距离是曼哈顿距离,所以这样的生成树被称为“曼哈顿最小生成树”。最小曼哈顿生成树有专用的算法 (2) ,求生成树时间复杂度可以仅为O

带修改莫队算法

我只是一个虾纸丫 提交于 2020-01-12 02:42:53
update in 2017.12.24: 以前写的≈shit,实在看不下去了,重写一遍 pre 很早之前就学习了莫队算法。 老师讲课的时候就提到过带修改莫队在线莫队树上莫队树上带修改莫队……但是一直都没有做到过有关的题, 今天有幸做了一道裸的带修改莫队的题, 那就来分享一下自己的经验 带修改的莫队 首先我们要知道,普通的莫队算法是不资瓷修改操作的, 不过后人对莫队算法加以改进 发明了资瓷修改的莫队算法 思路: 在进行修改操作的时候,修改操作是会对答案产生影响的(废话) 那么我们如何避免修改操作带来的影响呢? 首先我们需要把查询操作和修改操作 分别 记录下来。 在记录查询操作的时候,需要增加一个变量来记录 离本次查询最近的修改的位置 然后套上莫队的板子,与普通莫队不一样的是,你需要用一个变量记录当前已经进行了几次修改 对于查询操作,如果 当前改的比本次查询需要改的少 ,就改过去 反之如果改多了就改回来 说的听绕口的 比如,我们现在已经进行了3次修改,本次查询是在第5次修改之后,那我们就执行第4,5次修改 这样就可以避免修改操作对答案产生的影响了 code 题目链接 update in 2018.5.4 洛谷数据被加强了,块的大小开$sqrt(N)$会T飞,开$n^{\frac{2}{3}}$可无压力过,带修改莫队真是玄学 // luogu-judger-enable-o2 //

莫队算法学习

匿名 (未验证) 提交于 2019-12-03 00:34:01
慢慢的开始重新学习以前的算法了,先从莫队算法学起。ACM反正可以用板子,所以先功利一点,与AC题目无关的细节就不管了,以后有机会再补证明。 先来看板子题: CF86D. Powerful array 题意:给你n个数,m次询问,$K_s$为区间内s的数目,求区间[L,R]之间所有$K_s*K_s*s$的和。($1\leq n,m\leq 200000,a_i \leq 10^6$) 做法:先给序列分个块,按下标每$\sqrt{n}$个数分一块。然后将询问离线后排个序,排序的方法为先按L所在的块的标号从小到大排,对于L所在的块的标号相同的,按照R从小到大排。然后在一一走就好了。 复杂度比较好理解,为$O((m + n)\sqrt{n})$。 1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 typedef long long ll; 8 const int LEN = 2e5 + 5 ; 9 ll ans; 10 int i, j, k, n, m, s, t, S, l, r; 11 int a[LEN], pos[LEN], num[ 1000005 ]; 12

莫队算法

試著忘記壹切 提交于 2019-12-01 07:41:15
%%%莫队 优化暴力? 反正挺好用的 一些线段树树状数组很难维护的东西可以用莫队解决 区间修改就麻烦了。。。 当然你可以分块 #普通莫队: 询问如区间[l,r]中有多少不同的数,或出现次数最多的数出现的多少次,无修改 莫队较为适用的就是已知当前区间的答案能快速推出[l+1,r],[l-1,r],[l,r-1],[l,r+1] 将l和r类似指针一样在区间上扫,然后通过离线询问,给询问排序来降低指针移动次数,复杂度$O(n\sqrt{n})$ 排序: bel[a.l]==bel[b.l]?a.r<b.r:bel[a.l]<bel[b.l]; 奇偶性排序,对于同一个块,右端点单调上升或下降,波浪式移动减少移动次数: bel[a.l]<bel[b.l]||(bel[a.l]==bel[b.l]&&(bel[a.l]&1?a.r<b.r:a.r>b.r)); 如:小B的询问: 小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。 对于全部的数据,1<=N、M、K<=50000 直接莫队即可,开桶记录每个数在当前区间的出现次数,移动时减去即可 1 #include<iostream> 2 #include<cstdio> 3

莫队算法

丶灬走出姿态 提交于 2019-11-30 04:24:07
https://www.lydsy.com/JudgeOnline/problem.php?id=2038 题目描述 作为一个生活散漫的人,小 Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小 Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命 …… 具体来说,小 Z把这 N只袜子从 1到 N编号,然后从编号 L到 R(L 尽管小 Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。 你的任务便是告诉小 Z,他有多大的概率抽到两只颜色相同的袜子。当然,小 Z希望这个概率尽量高,所以他可能会询问多个 (L,R)以方便自己选择。 输入 输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。 输出 包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例) 样例输入 6 4 1 2 3 3 3 2 2 6 1 3 3 5 1 6 样例输出 2/5 0/1 1/1 4/15 莫队模板题,重点是学会分块排序,这才是核心

【学习笔记】莫队算法

与世无争的帅哥 提交于 2019-11-30 01:50:46
一.关于莫队   一种离线的关于区间操作的算法。   一般莫队会和以下知识点一起使用:   1.分块思想。   2.离散化。   3.树链剖分/倍增,用于应付树上的(然而本蒟蒻写这篇博客时还不会,后续补充) 二.算法实现   1.SPOJ 3267 D-Query     暴力做法:记录每个数字出现的次数,每个询问统计出现区间内次数不为0的数字个数。     复杂度:O(n 2 )     显然过不去。     考虑优化方法。每次遇到新数那么它出现次数一定是0,遇到旧数如果后面没有了那么它出现次数一定是1。     用双指针扫区间?     对于每个询问区间用指针移动来代替枚举即可实现上面的优化。     如果区间特别多怎么办?     考虑分块,按照块的下标和每个询问的右端点排序,那么就避免了双指针扫过去又扫回来的情况。     这就是莫队的基本操作了。     总复杂度:O(nlogn)+O(n√n)+O(n√n)=O(n√n)     同时这是一道莫队裸题。本题AC代码:(莫队模板) 1 #include<bits/stdc++.h> 2 using namespace std; 3 struct Query 4 { 5 int l,r,num,pos; 6 bool operator < (const Query &k) const 7 { 8 if(pos==k.pos)

莫队算法----区间求和

社会主义新天地 提交于 2019-11-29 21:31:11
连接 https://ac.nowcoder.com/acm/contest/1085/G 链接: https://ac.nowcoder.com/acm/contest/1085/G 来源:牛客网 题目描述 小sun最近突然对区间来了兴趣,现在他有这样一个问题想问问你: 给你n个数,每个数为 aia_i a i ​ ,现在有m个询问,每个询问l,r,需要求出: ∑i=lrai∗num(ai)\sum_{i=l}^r a_i*num(a_i) ∑ i = l r ​ a i ​ ∗ n u m ( a i ​ ) num(ai)num(a_i) n u m ( a i ​ )代表 aia_i a i ​ 在这个区间中出现的次数。 你能帮帮他吗? 输入描述: 第一行,两个整数n,m第二行,总共n个数,代表这个数列接下来m行,每行两个整数l,r,代表一个询问 输出描述: 输出总共m行,对于每个询问,输出这个询问对应的答案 示例1 输入 复制 10 5 1 3 2 4 5 6 4 5 6 7 1 5 2 5 3 4 1 10 3 7 输出 复制 15 14 6 73 29题目大意:在制定的一段区间内求arr[i]*cnt[arr[i]]之和,即一段区间内某一个数乘以该数字出现的次数。莫队算法 一篇讲的很好的博客 https://www.cnblogs.com/WAMonster/p