首页 > 其他分享 >莫队

莫队

时间:2022-10-06 16:57:17浏览次数:71  
标签:int 询问 pos 端点 排序 莫队

一.莫队(静态莫队)

我们以 Luogu P3901 数列找不同 为例讲一下静态莫队

这道题是个绿题,因为数据比较弱,但真是一道良心的莫队练手题

莫队是由前国家队队长莫涛发明的

莫队算法的精髓就是通过合理地对询问排序,然后以较优的顺序暴力回答每个询问。处理完一个询问后,可以使用它的信息得到下一个询问区间的答案。 优雅的暴力

考虑这个问题:对于上面这道题,如果我们知道区间[1,5]每个数的数量,如何求出[2,6]每个数的数量

算法 1 :暴力扫一遍。 (废话)

算法 2 :可以在区间[1,5]的基础上,去掉位置1(即将左端点右移一位),加上位置6(即将右端点右移一位),得到区间[2,6]的答案。

很明显,算法 2 要优于算法 1 ,我们给出这部分代码:

void add(int x){ 
      if(++t[a[x]] == 1) ++cnt; 
}
void del(int x){ 
      if(--t[a[x]] == 0) --cnt; 
}

//在获取答案时 
while(L<q[i].l) del(L++);
while(L>q[i].l) add(--L);
while(R<q[i].r) add(++R);
while(R>q[i].r) del(R--);

但是我们会发现如果按这样写,一种很简单的构造数据就能把时间复杂度把算法 2 也送走:先询问[1,2],再询问[99999,100000],多重复几次就TLE了。

我们有没有办法来加速呢?

这种暴力的耗时就耗在挪动次数上,我们要让他挪动的次数尽量少。

肯定是有的,我们将询问先储存下来,再按照某种方法排序,让他减少挪动的次数,这样会变快一些。

这种方法是强行离线,然后排序,所以这样的普通莫队不能支持修改。

那么怎么排序呢?

一种解决方式是优先按照左端点排序。

这种排序的方式,保证左端点只会向右挪,但是右端点每次最坏还是可以从最前面挪到最后面,从最后面挪到最前面,这样的复杂度还是 \(O(nm)\),是不行的。

我们的排序是要使左右端点挪动的次数尽量少,所以这里就有一种排序方法:

将序列分成 $ \sqrt{n} $ 个长度为 $ \sqrt{n} $ 的块(其实就是分块),排序第一关键字是询问的左端点所在块的编号,第二关键字是询问的右端点本身的位置,都是升序。然后我们用上面提到的“移动当前区间左右端点”的方法,按顺序求每个询问区间的答案,移动每一个询问区间左右端点可以求出下一个区间的答案。

这里先给出判断某一位置在哪个块中的代码:

int pos(int x){
	if(x % (int)sqrt(n)) return x/(int)sqrt(n)+1;
	return x/(int)sqrt(n);
}

这就是一般的莫队排序:

bool cmp(node a,node b){
	if(pos(a.l) == pos(b.l)){
		return a.r<b.r;
	}
    return pos(a.l)<pos(b.l);
}

但由于出题人过于毒瘤

又多出一种优化,叫做奇偶优化

按奇偶块排序。这也是比较通用的。如果区间左端点所在块不同,那么就直接按左端点从小到大排;如果相同,奇块按右端点从小到大排,偶块按右端点从大到小排。

bool cmp(node a,node b){
	if(pos(a.l) == pos(b.l)){
		if(pos(a.l)&1) return a.r<b.r;
		else return a.r>b.r;
	}
    return pos(a.l)<pos(b.l);
}

可是,这样做的复杂度是什么?

大约是 \(O(n \sqrt{n})\)。

Code:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int l,r,id;
};

int t[100010],n,m;
node q[100010];
int a[100010],cnt;
int ans[100010];

int pos(int x){
	if(x % (int)sqrt(n)) return x/(int)sqrt(n)+1;
	return x/(int)sqrt(n);
}

bool cmp(node a,node b){
	if(pos(a.l) == pos(b.l)){
		if(pos(a.l)&1) return a.r<b.r;
		else return a.r>b.r;
	}
    return pos(a.l)<pos(b.l);
}

void add(int x){ 
	if(++t[a[x]] == 1) ++cnt; 
}
void del(int x){ 
	if(--t[a[x]] == 0) --cnt; 
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>q[i].l>>q[i].r;
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int L=1,R=0;
	for(int i=1;i<=m;i++){
		while(L<q[i].l) del(L++);
		while(L>q[i].l) add(--L);
		while(R<q[i].r) add(++R);
		while(R>q[i].r) del(R--);
		ans[q[i].id] = (cnt == q[i].r-q[i].l+1) ? 1 : 0;
	}
	for(int i=1;i<=m;i++){
		printf(ans[i] ? "Yes\n" : "No\n");
	}
	return 0;
}

二、带修莫队

特点

  • 用于离线处理区间问题
  • 仅含单点修改
  • 能 \(O(1)\) 转移区间(和普通莫队一样)
  • 分块的每一块的大小是 \(n^{\frac{2}{3}}\)
  • 复杂度 \(O(n^{\frac{5}{3}})\)

带修改的莫队的询问排序方法为:

  • 第一关键字:左端点所在块编号,从小到大排序。
  • 第二关键字:右端点所在块编号,从小到大排序。
  • 第三关键字:经历的修改次数。也可以说是询问的先后,先询问的排前面。

对于前后两个区间的转移:

设当前询问为 \(a\),下一个询问为 \(b\),我们已知 \(a\),要求 \(b\)。

首先我们像普通莫队一样转移左右端点。

这时候我们可能会发现 \(a\) 和 \(b\) 的经历的修改次数不同

怎么办呢?

然而,莫队就是个优雅的暴力。

假如 \(a\) 较 \(b\) 少修改了 \(p\) 次,那我们就把这 \(p\) 次修改一个一个从前往后暴力地加上去。假如 \(a\) 较 \(b\) 多修改了 \(q\) 次,那我们就把这 \(q\) 次修改从后往前还原掉。

具体代码与普通莫队大同小异,这里不再给出。
例题:P1903 [国家集训队]数颜色 / 维护队列

标签:int,询问,pos,端点,排序,莫队
From: https://www.cnblogs.com/ycw123/p/16757959.html

相关文章

  • 【luogu P5906】【模板】回滚莫队&不删除莫队
    【模板】回滚莫队&不删除莫队题目链接:luoguP5906题目大意给你一个序列,多次询问每次问一个区间,求里面相同的数的最远间隔距离。思路考虑莫队,发现加入一个点好处理,但是......
  • 2021杭电多校1 J (普通莫队 树状数组)
    题意:给出1e5个二维平面上的坐标点0<=(x,y)<=1e5,1e5个询问,每次问x0,y0到x1,y1的矩阵中有多少y值不同的坐标点。思路:操作只有询问,不强制在线,数据范围1e5,就差把莫......
  • 普通莫队解决区间众数的个数
    SP32900KDOMINO-K-dominantarrayP1997faebdc的烦恼P3709大爷的字符串题被摩尔投票法洗脑半天,发现好像可以直接拿莫队写啊喂!针对原数据离散化,后开一个计数器和一......
  • 【luogu CF633H】Fibonacci-ish II(莫队)(线段树)(矩阵乘法)
    Fibonacci-ishII题目链接:luoguCF633H题目大意给你一个序列,每次问你一个区间,把里面的数拿出来去重排序,第i个位置乘上斐波那契数列第i项之后所有数的和。思路这题......
  • CF633H Fibonacci-ish II 莫队 线段树 矩阵
    CF633HFibonacci-ishII题意很简明同时给人以不可做感。直接暴力大概是\(n^2log\)的优化一下提前排好序从小到大枚举数字再枚举询问可以完成\(n^2\)经过精细的优化......
  • P5838 [USACO19DEC]Milk Visits G 【树上莫队】
    P5838[USACO19DEC]MilkVisitsG给定一颗树,每个点有点权,\(m\)次询问,每次求\(u\)到\(v\)之间的路径是否出现过权值为\(w\)的点。树上莫队板子题,我们需要一个dfs......
  • 带修莫队例题详解
    带修莫队[P1903国家集训队]数颜色/维护队列版本更新内容:在普通莫队基础上增加时间坐标,提高游戏难度;排序时以时间坐标为第三关键字,奇偶排序玄学值上调\(20\%\);代......
  • dfs序 括号序 欧拉序 树上莫队 虚树建立 傻傻分不清
    括号序进加一次,出加一次,显然最后得到的序列只有\(2n\)个点。voiddfs(intx){ in[x]=++tot; for(y)dfs(y); out[x]=++tot;}显然我们希求将树上路径表示为区......