RMQ问题—ST表

RMQ问题,即区间最值问题
tarjan发明的基于倍增的st表算法
可以在 O ( n l o g n ) 的时间内求解静态RMQ问题

d p [ i ] [ j ] 表示序列区 [ i , i + 2 j ] 内的最值
初始化 d p [ i ] [ 0 ] = a [ i ]

那么dp数组就有递推式
d p [ i ] [ j ] = m a x ( d p [ i ] [ j 1 ] , d p [ i + ( 1 << j 1 ) ] [ j 1 ] )
即区间 [ i , i + 2 j ] 中的最值等于区间 [ i , i + 2 j 1 ] [ i + 2 j 1 , j ] 中的最值

for(int i=1;i<=n;i++)
dp[i][0]=a[i];

for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);

当我们查询区间 [ l l , r r ] 中的最值时
可以枚举一个断点k
比较区间 [ l l , l l + 2 k ] [ r r 2 k + 1 , r r ] 得到答案

int k=0;
while((1<<k+1)<=rr-ll+1)k++;
ans=max(dp[ll][k],dp[rr-(1<<k)+1][k]);

虽然两个区间可能会重叠,但是这并不影响结果,因为重叠部分也属于 [ l l , r r ]
这也是用于比较的第二个区间是 [ r r 2 k + 1 , r r ] 的原因
如果第二个区间是 [ l l + 2 k , l l + 2 k + 2 k ] 将有可能越出rr的边界


洛谷P3865 【模板】ST表

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

int n,m;
int d[1000010];
int dp[1000010][55];

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
    d[i]=read();

    for(int i=1;i<=n;i++)
    dp[i][0]=d[i];
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i+(1<<j)-1<=n;i++)
    dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);

    while(m--)
    {
        int ll=read(),rr=read();
        int k=0;
        while((1<<k+1)<=rr-ll+1)k++;
        int ans=max(dp[ll][k],dp[rr-(1<<k)+1][k]);
        print(ans);printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/80777570
今日推荐