二維RMQ問題
阿新 • • 發佈:2018-12-19
前置知識
問題引入
- 題目地址IN
對於一個的矩陣,每個格子有一個值,有個詢問,每次詢問你一個子矩陣中的最大值。
- 暴力
每次花子矩陣大小的複雜度去查詢。 複雜度最壞
- 改進
我們用棵線段樹或者樹狀陣列來維護每行區間最大值,複雜度最壞,還是沒法過,有沒有更快的方法呢?
- 分析
既然是靜態的詢問,沒有修改操作,那麼很容易聯想到中的表。
那麼暴力的表就是和資料結構的做法類似,預處理個表,每次在多個表中查詢最大值,複雜度最壞為,雖然能過更多的資料,但是還是不夠。
- 二維表
既然查詢物件是個二維矩陣,那麼我們能不能維護一個二維的表呢?答案顯然是肯定的。
預處理:
所以我們令,為新的表,表示以為左上角,右下角為,那麼我們可以看出預處理的複雜度會是,所以對於詢問數交少的還是用資料結構預處理查詢比較好。
然後我們來看,對於每個,可以由哪些狀態更新。
我們來看這個狀態表示的矩陣,如下圖:
假設這裡左上角的點為,右下角點為,那麼我們可以把它分成兩部分,如下圖:
那麼我們將點看作,其實原來的大矩陣就可以由分成的這兩個小矩陣更新得到,轉移如下:
其中,裡面第一個為上半部分矩陣,後面一個為下半部分矩陣。
如果的話,就將其豎起剖成兩部分即可,是同理的。轉移如下:
所以最後按照從小到大更新即可。
查詢
對於一個子矩陣,我們假設它的左上角座標為,右下角為,那麼可以通過預處理的二維表,將其分成四部分查詢,如下圖:
其中四個部分為圖中,查詢區間是可以重合的。
其實就對應瞭如下四個預處理的狀態,我們令:
答案就為上面四個矩陣的,所以預處理對數,每次回答即可。
那麼總的複雜度為,是可以過的了。
程式碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Log=12;
const int N=610;
const int inf=1e9;
int n,m,Q;
int maxv[Log][Log][N][N];
int pre[N],val[N][N];
void init(){
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)maxv[0][0][i][j]=val[i][j];
pre[2]=pre[3]=1;
for(int i=4,up=max(n,m);i<=up;i++)pre[i]=pre[i>>1]+1;
int up1=pre[n]+1,up2=pre[m]+1;
for(int l1=0;l1<=up1;l1++){
for(int l2=0;l2<=up2;l2++){
if(!l1&&!l2) continue;
for(int i=1;(i+(1<<l1)-1)<=n;i++){
fo