自学内容网 自学内容网

细说简单简单莫队

看来看去,似乎已经好久没有写博客了,今天终于是抽出空来,讲一讲莫队。

莫队算法的基本介绍

在信竞得路上,我们时常遇到一些题目让我们求解一段区间里的颜色个数有多少啊,这个区间内的最大值是多少啊?诸如此类的问题常常让我们这些菜鸡束手无策,所以莫队算法横空出世!

莫队算法是由莫涛提出的算法。在莫涛提出莫队算法之前,莫队算法已经在 Codeforces 的高手圈里小范围流传,但是莫涛是第一个对莫队算法进行详细归纳总结的人。莫涛提出莫队算法时,只分析了普通莫队算法,但是经过 OIer 和 ACMer 的集体智慧改造,莫队有了多种扩展版本,想什么待修莫队之类的。

离线算法

什么是离线算法呢,简单来说就是先把所有的输入都输入后,在进行这些输入的一定的规律来进行时间复杂度更加低的运算,运用这样的算法除了我们今天要学习的莫队以外,还有像CDQ分治,整体二分等等。

正片——莫队算法

思路

首先对于一个区间 [ l , r ] [l,r] [l,r] 它是可以 O ( 1 ) O(1) O(1) 扩展到 [ l + 1 , r ] , [ l , r + 1 ] [l+1,r],[l,r+1] [l+1,r],[l,r+1] 等的位置,那么如果我们把现在所要查询的区间按照这样的规律进行排序,那么我们是不是就可以节省重新计算的时间了?
换句话说,就是在排好序后若 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 在一组的话,那么 [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 就是可以把一部分的答案从 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 一步一步暴力的转移过来的。

如果这样进行转移的话,我们就可以在 O ( n n ) \cal{O(n\sqrt n)} O(nn ) 的时间复杂度内完成计算。

处理

如何排序

我上述的内容都是建立在已经对要处理的区间进行排序的前提下的,所以我们接下来考虑的就是要怎么排序。

首先一定是对左端点进行排序,然后按照右端点的长短从小到大排序,这是没有问题的对吧。
又因为要保证事件复杂度为 O ( n n ) \cal{O(n\sqrt n)} O(nn ) ,所以我们在绝大部分的时候是要把左端点按照 n \sqrt n n 的长度进行分组,也就是是说当 l 1 n = l 2 n \frac{l_1}{\sqrt n}=\frac{l_2}{\sqrt n} n l1=n l2 的时候,就代表 l 1 l_1 l1 l 2 l_2 l2 是同一组的,此时就按照右端点排序,否则按照左端点排序。

struct Node{
int l,r;
};
k=sqrt(n);
bool cmp(const Node a1,const Node a2){
return a1.l/k==a2.l/k?a1.r<a2.r:a1.l<a2.l;
} 

如何分块

但是这里要注意一个点,如果每次都是将块长分为 n \sqrt n n 的话,在有些时候是不对的,或者说是不优秀的,如下:

我们设 m m m 为询问次数, n n n 为总长度,当 m m m n \sqrt n n 同阶的时候,最优的时间复杂度应该是 O ( n ) \cal{O (n)} O(n) ,但是如果此时把块长设为了 n \sqrt n n 的话,就会导致时间复杂度为 O ( n n ) \cal{O (n\sqrt n)} O(nn )

对于此,我们可以进行如下的分析我们设块长度为 S S S,那么对于任意多个在同一块内的询问,挪动的距离就是 n n n,一共 n S \displaystyle \frac{n}{S} Sn 个块,移动的总次数就是 n 2 S \displaystyle \frac{n^2}{S} Sn2,移动可能跨越块,所以还要加上一个 m S mS mS 的复杂度,总复杂度为 O ( n 2 S + m S ) \displaystyle O\left(\frac{n^2}{S}+mS\right) O(Sn2+mS),我们要让这个值尽量小,那么就要将这两个项尽量相等,发现 S S S n m \displaystyle \frac{n}{\sqrt{m}} m n 是最优的(证明见附1),此时复杂度为 O ( n 2 n m + m ( n m ) ) = O ( 2 n m ) = O ( n m ) \displaystyle O\left(\frac{n^2}{\displaystyle \frac{n}{\sqrt{m}}}+m\left(\frac{n}{\sqrt{m}}\right)\right)=O(2n\sqrt{m})=O(n\sqrt{m}) O m nn2+m(m n) =O(2nm )=O(nm )

示范代码

int lim=sqrt(n);

bool cmp(const Node a1,const Node a2){
return a1.l/lim==a2.l/lim?a1.r<a2.r:a1.l<a2.l;
} 

sort(v+1,v+1+m,cmp);
int nowl=1,nowr=0,tot=0;
for (int i=1;i<=m;i++){
int l1=v[i].l,r1=v[i].r;
while (nowl>l1){
nowl--;
update():
}
while (nowr<r1){
nowr++;
update();
}
while (nowl<l1){
update():
nowl++;
}
while (nowr>r1){
update();
nowr--;
}
}

莫队的优化

我们来看下面这组的数据

1 1
2 100
3 1
4 100

我们假设块长为2,那么发现他来来回回跑了300次左右,这是因为他在不同的块中反复移动,那么我们这里就可以采用莫队的玄学优化方法——奇偶性优化!
奇偶性优化指的是对于奇数块的询问, r r r 从小到大排序,对于偶数块排序,则按照 r r r 从大到小进行排序。这样的话,可以让程序快30%左右!

bool cmp(const Node a1,const Node a2){
if (a1.l/k==a2.l/k){
if (a1.l/k&1)return a1.r<a2.r;//a1.l/k&1 等价于 (a1.l/k)%2==1 
else return a1.r>a2.r;
}else return a1.l/k<a2.l/k;
}

例题

题目传送门——小Z的袜子
这题是比较裸的,可以直接上莫队,关键就是要推出转换的关系。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF=5e4+10;
struct f{
ll l,r;
ll id,len;
}v[INF];
ll a[INF],k,tot,cnt[INF];
ll ans1[INF],ans2[INF];

bool cmp(const f a1,const f a2){
return a1.l/k==a2.l/k?a1.r<a2.r:a1.l<a2.l;
}

void add(int x){
cnt[a[x]]++;
if (cnt[a[x]]>=2)
tot+=cnt[a[x]]*(cnt[a[x]]-1)-(cnt[a[x]]-1)*(cnt[a[x]]-2);
}

void del(int x){
cnt[a[x]]--;
if (cnt[a[x]]>0)
tot-=(cnt[a[x]]+1)*(cnt[a[x]])-cnt[a[x]]*(cnt[a[x]]-1);
}
int main(){
int n,m;
cin>>n>>m;
k=sqrt(n);
for (int i=1;i<=n;i++){
cin>>a[i];
}
for (int i=1;i<=m;i++){
cin>>v[i].l>>v[i].r;
v[i].len=(v[i].r-v[i].l+1);
v[i].id=i;
}
sort(v+1,v+1+m,cmp);
int nowl=1,nowr=0;
for (int i=1;i<=m;i++){
int l1=v[i].l,r1=v[i].r;
while (nowl<l1){
del(nowl);
nowl++;
}
while (nowl>l1){
nowl--;
add(nowl);
}
while (nowr<r1){
nowr++;
add(nowr);
}
while (nowr>r1){
del(nowr);
nowr--;
}
if (tot==0){
ans1[v[i].id]=0;
ans2[v[i].id]=1;
}else {
ll A=tot,B=v[i].len*(v[i].len-1);
ll g=__gcd(A,B);
ans1[v[i].id]=A/g,ans2[v[i].id]=B/g;
}
}
for (int i=1;i<=m;i++){
cout<<ans1[i]<<"/"<<ans2[i]<<endl;
}
return 0;
}


  1. 我们知道均值不等式指 a + b ≥ 2 a b a+b \ge 2\sqrt{ab} a+b2ab ,那么对于 n 2 S + m S \displaystyle \frac{n^2}{S}+mS Sn2+mS n 2 S + m S ≥ 2 n 2 S × m S = 2 n 2 m = 2 n m \displaystyle \frac{n^2}{S}+mS \ge 2\sqrt{\displaystyle \frac{n^2}{S}\times mS}=2\sqrt{n^2m}=2n\sqrt{m} Sn2+mS2Sn2×mS =2n2m =2nm
    那么等号当且仅当 n 2 S = m S \displaystyle \frac{n^2}{S}=mS Sn2=mS 的时候取到,解这个方程得到 S = n m S=\displaystyle \frac{n}{\sqrt{m}} S=m n ↩︎


原文地址:https://blog.csdn.net/CylMK/article/details/144058574

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!