ST表


ST表(稀疏表)

概述

ST表(Sparse Table,稀疏表)是一種主要用於求解可重復貢獻問題的數據結構,它基於倍增思想,通過預處理做到快速在線查詢,不支持修改

其中可重復貢獻問題是指對於一個元素來說,重復計算多次得到的答案不會改變的問題,例如求區間最值(RMQ),求區間最大公約數 (\(\gcd\))等,然而區間和,區間積等問題則不屬於可重復貢獻問題

過程

預處理

下面我們以區間最大值問題為例,介紹ST表的具體實現

問題:給定長度為 \(n\) 的序列和 \(q\) 次詢問,每次詢問給出 \(l,r\) ,你需要給出序列 \([l,r]\) 區間的最大值

數據范圍:對於 \(100\%\) 的數據,滿足 \(n\in[1,10^5]\)\(q\in[1,2\times10^5]\)\(l,r\in[1,n]\),序列中的任一元素 \(a_i\in[1,10^9]\)

點我傳送 Luogu P3865 【模板】ST 表

首先我們考慮暴力,對於每次詢問都掃描 \([l,r]\),時間復雜度 \(\Theta(nm)\) 顯然T得飛起

那么該怎么解決呢?

考慮預處理出一部分區間的 \(max\),在詢問時只需要將覆蓋區間的 \(max\) 疊加就可以得到答案

那么應該預處理哪些區間呢?

考慮利用倍增思想,每次預處理出以 \(i\) 為起點,往后數 \(2^j\) 個數的區間 \(max\),也就是 \([i,i+2^j-1]\) 的區間 \(max\),不妨設 \(f_{i,j}\) 表示以 \(i\) 為起點,往后數 \(2^j\) 個數的區間 \(max\)

怎么合並出更大的區間呢?

可以想到把 \(2^j\) 拆分為兩個 \(2^{j-1}\) 相加,這里可能有些抽象,我們畫圖來進行更好的理解

image

那么有 \(f_{i,j}=max(f_{i,j-1},f_{i+2^{j-1},j-1})\),類似於DP,只需要遞推求解即可

由於需要大量計算對數,每次都使用 \(\log\) 函數十分低效,我們可以把 \(\log_2\) 全部預處理出來,使用的時候直接調用即可,具體方法見代碼

c++ code

#define MAXN 100005
int n,f[MAXN][21],lg2[MAXN];//記得定義為全局變量,初始值才是0
for(int i=2;i<=n;++i) lg2[i]=lg2[i>>1]+1;//預處理log_2
for(int i=1;i<=n;++i) scanf("%d",&f[i][0]);//f[i][0]直接讀入即可
for(int j=1;j<=lg2[n];++j)//最外層枚舉j,相當於每次都翻倍
    for(int i=1,tmp=(1<<j)-1;i+tmp<=n;++i)//內層枚舉i,遍歷這一層數組,從上一層更新
        f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

查詢

對於每一次詢問區間 \([l,r]\),我們要怎么做才能處理出最大值呢?

我們先計算出滿足 \(2^k\le r-l+1<2^{k+1}\)\(k\),也就是讓 \(2^k\) 小於區間長度的最大的 \(k\),這樣“從 \(l\) 開始的 \(2^k\) 個數”以及“以 \(r\) 結尾的 \(2^k\) 個數”就一定能覆蓋整個詢問區間 \([l,r]\),也就是“從 \(l\) 開始的 \(2^k\) 個數”以及“從 \(r-2^k\)+1 開始的 \(2^k\) 個數”

為什么呢?假設這兩段無法完全覆蓋 \([l,r]\) 那么有 \(2\times2^k<|[l,r]|\),那么 \(k\) 就不是讓 \(2^k\) 小於區間長度的最大的 \(k\),與我們的定義矛盾

“從 \(l\) 開始的 \(2^k\) 個數”以及“以 \(r\) 結尾的 \(2^k\) 個數”這兩段區間中可能會有重疊的部分,計算時會被重復統計,這就是為什么ST表只能用於處理可重復貢獻問題

同樣,再次上圖幫助理解

image

對於每次詢問,\(f_{l,k}\)\(f_{r-2^k+1,k}\) 的最大值就是 \([l,r]\) 的最大值

c++ code

int q,l,r,k;
scanf("%d",&q);
while(q--)//一共q次詢問
{
    scanf("%d%d",&l,&r);
    k=lg2[r-l+1];
    printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));
}

復雜度分析

(注意!以下復雜度分析僅僅適用於RMQ問題,而區間最大公約數的復雜度不在此討論)

預處理時雙層循環,外層枚舉 \(j\),一共枚舉 \(log_2n\) 次,內層枚舉 \(i\),對於每一個 \(j\) 都要枚舉 \(n\) 次,所以預處理總復雜度為 \(\Theta(n\log n)\)

對於每個詢問,只用求一次 \(\max\),復雜度為 \(\Theta(1)\)

小結

ST表是一種主要用於求解可重復貢獻問題的數據結構,盡管能維護的東西有限並且不支持修改操作,但是因為其代碼簡短,思想清晰,使用方便的特點,在很多地方都有應用,例如求最近公共祖先等等

利用分塊思想,笛卡爾樹等其他算法或思想還可以對ST表進行優化或拓展,感興趣的讀者可以自行查閱


該文為本人原創,轉載請注明出處

博客園傳送門

洛谷傳送門


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM