1. 程式人生 > 實用技巧 >SMZX十日遊(第二階段RMQ)RMQ學習筆記

SMZX十日遊(第二階段RMQ)RMQ學習筆記

創作背景

今天是在SMZX的第四天,終於學習新知識了,感動。所以,當然要寫部落格好好總結一番

RMQ原理

這裡有一個小問題:

有N個數,M次詢問,每次給定區間[L,R],求區間內的最大值。
N<=10,M<=10

暴力打擂臺就可以了
倘若把問題改一改呢:

N<=1e5,M<=1e5

正常的暴力就會分分鐘\(TLE\)警告
那怎麼解決呢:

\[RMQ \]

舉個例子:

如果想要知道某個2的冪的區間最大值直接提取就可以了,而這個表就被稱為:\(ST表\)
那如果不是2的冪ST表是不是就廢了?
別急,馬上就講它有什麼用:
聽過max函式吧,其實他有一個特性:

\[操作允許區間重疊 \]

就是max(a,b,c)=max(max(a,b),max(b,c))
這很重要,決定了能不能用RMQ
有了這個特性,就可以為所欲為 用RMQ了

還是那個例子,現在求的是7,2,3這個序列,根據剛剛的特性,max(7,2,3)=max((7,2)(7,3))
等等,(7,2)(7,3)熟不熟悉,這不就是被塗紅的那兩項嗎?
所以搜尋區間最大值的時候只需要找左端點和右端點的ST表即可

Code

#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
int a[100001];
int maxx[100001][50];
int minn[100001][50];
int n,m,l,r;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    	cin>>maxx[i][0];
    
    for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
			if(i+(1<<j)-1<=n)		
            	maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]);
    for(int i=1;i<=m;i++)
    {
        cin>>l>>r;
        int len=log2(r-l+1);
        printf("%d ",max(maxx[l][len],maxx[r-(1<<len)+1][len]));
    }
    return 0;
}

小試牛刀

題目傳送門:忠誠

題目描述

老管家是一個聰明能幹的人。他為財主工作了整整10年,財主為了讓自已賬目更加清楚。要求管家每天記k次賬,由於管家聰明能幹,因而管家總是讓財主十分滿意。但是由於一些人的挑撥,財主還是對管家產生了懷疑。於是他決定用一種特別的方法來判斷管家的忠誠,他把每次的賬目按1,2,3…編號,然後不定時的問管家問題,問題是這樣的:在a到b號賬中最少的一筆是多少?為了讓管家沒時間作假他總是一次問多個問題。

輸入格式

輸入中第一行有兩個數m,n表示有m(m<=100000)筆賬,n表示有n個問題,n<=100000。
第二行為m個數,分別是賬目的錢數
後面n行分別是n個問題,每行有2個數字說明開始結束的賬目編號。

輸出格式

輸出檔案中為每個問題的答案。具體檢視樣例。

輸入輸出樣例

輸入

10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10

輸出

2 3 1

思路

az,不就是RMQ的模板題麼
上程式碼:

Code

#include<bits/stdc++.h>
using namespace std;
int a[100001];
int minn[100001][50];
int n,m,l,r;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    	cin>>minn[i][0];
    
    for(int j=1;j<=20;j++)//初始化,2的20次方
        for(int i=1;i<=n;i++)
			if(i+(1<<j)-1<=n)		
            	minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);//還記得嗎,這裡就是在左右兩個區間進行求最小值
    for(int i=1;i<=m;i++)
    {
        cin>>l>>r;
        int len=log2(r-l+1);
        printf("%d ",min(minn[l][len],minn[r-(1<<len)+1][len]));//求左右兩個區間的最小值
    }
    return 0;
}

THE END

其實RMQ,理解好了就不難,但是理解要花很長時間
感謝給予我啟發的題解