【训练题26:ST表实现RMQ(附带详细证明)】质量检测 | 洛谷 P2251

质量检测 | 洛谷 P2251

题外话

之前听说过ST表,但是没想到这么有用又这么简单。。

前置知识

  • DP,动态规划

难度

普 及 / 提 高 − \color{orange} 普及/提高- /

题意

给你一个长度为 N N N 的序列 A N A_N AN和一个 M M M
让你输出 Q i = min ⁡ { A i , A i + 1 ⋯ A i + M − 1 } Q_i=\min\{A_i,A_{i+1}\cdots A_{i+M-1}\} Qi=min{ Ai,Ai+1Ai+M1}
其中 i = 1   t o   N − m + 1 i=1\ to\ N-m+1 i=1 to Nm+1

数据范围

M ≤ N ≤ 1 0 5 M\le N\le 10^5 MN105
A i ≤ 1 0 6 A_i\le 10^6 Ai106

思路

  • ST表能干什么

    • 给你一个静态序列,能 O ( 1 ) O(1) O(1) 算出其中任何一个连续子序列中的最值。
  • 相比于其他的一些做法,ST表有一些优势,适用于静态的多查询的情况。

    • 时间复杂度 :
      • 预处理: O ( N log ⁡ N ) O(N\log N) O(NlogN)
      • 查询: O ( 1 ) O(1) O(1)
    • 空间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)
  • 缺点:离线算法,无法更新序列

  • 做法:设 d p [ i ] [ j ] = min ⁡ { A i , A i + 1 ⋯   , A i + 2 j − 1 } dp[i][j]=\min\{A_i,A_{i+1}\cdots,A_{i+2^j-1}\} dp[i][j]=min{ Ai,Ai+1,Ai+2j1}记作 min ⁡ [ i , i + 2 j − 1 ] \min[i,i+2^j-1] min[i,i+2j1]

    扫描二维码关注公众号,回复: 12473688 查看本文章
    • 初始化: d p [ i ] [ 0 ] = A i \color{red}dp[i][0]=A_i dp[i][0]=Ai
    • 预处理: d p [ i ] [ j ] = min ⁡ { d p [ i ] [ j − 1 ] , d p [ i + 2 j − 1 ] [ j − 1 ] } \color{red}dp[i][j]=\min\{dp[i][j-1],dp[i+2^{j-1}][j-1]\} dp[i][j]=min{ dp[i][j1],dp[i+2j1][j1]}
      • 证明: min ⁡ [ i , i + 2 j − 1 ] = min ⁡ { min ⁡ [ i , i + 2 j − 1 − 1 ] , min ⁡ [ i + 2 j − 1 , i + 2 j − 1 + 2 j − 1 − 1 ] } = min ⁡ { min ⁡ [ i , i + 2 j − 1 − 1 ] , min ⁡ [ i + 2 j − 1 , i + 2 j − 1 ] } \begin{aligned}\min[i,{i+2^j-1}]&=\min\{\min[i,{i+2^{j-1}-1}],\min[{i+2^{j-1}},{i+2^{j-1}+2^{j-1}-1}]\}\\&=\min\{\min[i,{i+2^{j-1}-1}],\min[{i+2^{j-1}},{i+2^{j}-1}]\}\end{aligned} min[i,i+2j1]=min{ min[i,i+2j11],min[i+2j1,i+2j1+2j11]}=min{ min[i,i+2j11],min[i+2j1,i+2j1]}
      • 后面两个区间的并就是前面的区间,显然成立。
    • 查询 [ l , r ] [l,r] [l,r] 区间内的最小值 min ⁡ [ l , r ] \min[l,r] min[l,r]
      • 首先求出 k = log ⁡ 2 ( r − l + 1 ) \color{red}k=\log_2(r-l+1) k=log2(rl+1)
      • 然后答案为 min ⁡ { d p [ l ] [ k ] , d p [ r − 2 k + 1 ] [ k ] } \color{red}\min\{dp[l][k],dp[r-2^k+1][k]\} min{ dp[l][k],dp[r2k+1][k]}
        • 证明:只要求出这两个范围覆盖全区间即可
        • 即证明: [ l , l + 2 k − 1 ] ∩ [ r − 2 k + 1 , r − 1 ] = [ l , r ] [l,l+2^k-1]\cap[r-2^k+1,r-1]=[l,r] [l,l+2k1][r2k+1,r1]=[l,r]
        • 即证明: l + 2 k − 1 ≥ r − 2 k + 1 l+2^k-1\ge r-2^k+1 l+2k1r2k+1恒成立。(因为边界已经成立)
        • 即证明: 2 k + 1 ≥ r − l + 2 2^{k+1}\ge r-l+2 2k+1rl+2,带入 k = log ⁡ 2 ( r − l + 1 ) k=\log_2(r-l+1) k=log2(rl+1)
        • 即证明: 2 r − 2 l + 2 ≥ r − l + 2 2r-2l+2\ge r-l+2 2r2l+2rl+2
        • 显然成立 Q.E.D.

核心代码

  • 时间复杂度 :
    • 预处理: O ( N log ⁡ N ) O(N\log N) O(NlogN)
    • 查询: O ( 1 ) O(1) O(1)
  • 空间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)
/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 1e5+50;
int aa[MAX];
int dp[MAX][100];
int n,m;
void init(){
    
    
    for(int i = 1;i <= n;++i)dp[i][0] = aa[i];
    for(int j = 1;(1<<j) <= n;++j){
    
    
        for(int i = 1;i+(1<<j)-1 <= n;++i){
    
    
            dp[i][j] = min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
        }
    }
}
int query(int l,int r){
    
    
    int k = log2(m);
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main(){
    
    

    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++i)scanf("%d",&aa[i]);
    init();
    for(int i = m;i <= n;++i){
    
    
        printf("%d\n",query(i-m+1,i));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/112307285