数据结构 - ST表 - 处理RMQ问题


数据结构 - ST表 - 处理RMQ问题

1、ST表

是 一 种 用 于 解 决 R M Q ( R a n g e   M i n i m u m / M a x i m u m   Q u e r y , 即 区 间 最 值 查 询 ) 问 题 的 数 据 结 构 。 是一种用于解决RMQ(Range \ Minimum/Maximum \ Query,即区间最值查询)问题的数据结构。 RMQ(Range Minimum/Maximum Query,)

核 心 思 想 : 倍 增 。 将 一 段 区 间 平 均 一 分 为 二 , 以 两 个 子 区 间 的 最 值 更 新 当 前 区 间 的 最 值 。 核心思想:倍增。将一段区间平均一分为二,以两个子区间的最值更新当前区间的最值。

定 义 S T 表 , f [ i ] [ j ] : 区 间 [ i , i + 2 j − 1 ] 内 的 最 值 。 定义ST表,f[i][j]:区间[i,i+2^j-1]内的最值。 STf[i][j][i,i+2j1]

更 新 区 间 [ i , j ] , 可 对 子 区 间 [ i , i + 2 j − 1 − 1 ] 和 区 间 [ i + 2 i − 1 , i + 2 j − 1 ] 取 最 值 。 更新区间[i,j],可对子区间[i,i+2^{j-1}-1]和区间[i+2^{i-1},i+2^j-1]取最值。 [i,j][i,i+2j11][i+2i1,i+2j1]

则 有 f [ i ] [ j ] = m a x ( f [ i ] [ j − 1 ] , f [ i + 2 j − 1 ] [ j − 1 ] ) 。 则有f[i][j]=max(f[i][j-1],f[i+2^{j-1}][j-1])。 f[i][j]=max(f[i][j1],f[i+2j1][j1])

由 于 l o g 2 x > x 2 , 故 查 询 区 间 [ l , r ] 的 最 值 时 , 记 区 间 长 度 t = r − l + 1 。 由于log_2x>\frac{x}{2},故查询区间[l,r]的最值时,记区间长度 t=r-l+1。 log2x>2x[l,r]t=rl+1

可 返 回 m a x ( f [ l ] [ l o g 2 t ] , f [ r − t + 1 ] [ l o g 2 t ] ) 。 可返回max(f[l][log_2t],f[r-t+1][log_2t])。 max(f[l][log2t],f[rt+1][log2t])

参考——《ST表》。

模板:

int f[N][M];   //f[i][j]:区间[i,i+2^j-1]内的最值

void Init()    //预处理2^k和logk
{
    for(int i=0;i<M;i++) pow_2[i]=1<<i;
    
    log_2[0]=-1;
    for(int i=1;i<=n;i++) log_2[i]=log_2[i/2]+1;
}

void get_ST()
{
    for(int i=1;i<=log_2[n];i++)   //先枚举区间长度
        for(int j=1;j+pow_2[i]-1<=n;j++)
                f[j][i]=max(f[j][i-1],f[j+pow_2[i-1]][i-1]);
}

int query(int l,int r)
{
    int k=log_2[r-l+1];
    return max(f[l][k],f[r-pow_2[k]+1][k]);
}

例题2-1:洛谷 P3865 - ST表

题目背景
这是一道ST表经典题——静态区间最大值

请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)

题目描述
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。

输入格式:
第一行包含两个整数 N, M,分别表示数列的长度和询问的个数。

第二行包含 N 个整数(记为 a i a_i ai),依次表示数列的第 i 项。

接下来 M 行,每行包含两个整数 l i , r i l_i, r_i li,ri,表示查询的区间为 [ l i , r i ] [ l_i, r_i] [li,ri]

输出格式:
输出包含 M 行,每行一个整数,依次表示每一次询问的结果。

输入样例:

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

输出样例:

9
9
7
7
9
8
7
9

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1e5+10,M=20;

int n,m;
int pow_2[M],log_2[N];
int f[N][M];   //f[i][j]:区间[i,i+2^j-1]内的最值

void Init()
{
    for(int i=0;i<M;i++) pow_2[i]=1<<i;
    
    log_2[0]=-1;
    for(int i=1;i<=n;i++) log_2[i]=log_2[i/2]+1;
}

void get_ST()
{
    for(int i=1;i<=log_2[n];i++)   //先枚举区间长度
        for(int j=1;j+pow_2[i]-1<=n;j++)
                f[j][i]=max(f[j][i-1],f[j+pow_2[i-1]][i-1]);
}

int query(int l,int r)
{
    int k=log_2[r-l+1];
    return max(f[l][k],f[r-pow_2[k]+1][k]);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&f[i][0]);
    
    Init();
    get_ST();
    
    int l,r;
    while(m--)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",query(l,r));
    }
    
    return 0;
}

例题2-2:The Water Problem - HDU - 5443

题目:

水是非常有限的资源。人们总是争取最大的水资源。给定一系列具有 a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1a2a3...an的水资源,每个数表示水资源的大小。给定一组查询,每个查询包含2个整数 l l l r r r ,请找出 l l l r r r 之间最大的水源。(包含l和r)

Input

首先给出一个整数 T ( T ≤ 10 ) T(T≤10) T(T10),表示测试用例的数量。对于每个测试用例,在表示水源数量的行上有 n ( 0 ≤ n ≤ 1000 ) n(0≤n≤1000) n(0n1000) n n n 个整数分别表示 a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1a2a3...an 1 ≤ a i ≤ 1 0 6 1≤a_i≤10^6 1ai106。在下一行,有一个数字 q ( 0 ≤ q ≤ 1000 ) q(0≤q≤1000) q(0q1000)表示查询的数量。之后,将有 q q q行,其中两个整数 l l l r ( 1 ≤ l ≤ r ≤ n ) r(1≤l≤r≤n) r(1lrn)表示您应该找到最大水源的范围。

Output

对于每次查询,输出查询区间中最大的水资源。

Sample Input

3
1
100
1
1 1
5
1 2 3 4 5
5
1 2
1 3
2 4
3 4
3 5
3
1 999999 1
4
1 1
1 2
2 3
3 3

Sample Output

100
2
3
4
4
5
1
999999
999999
1

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1e3+10,M=15;

int n,m,T;
int pow_2[M],log_2[N];
int f[N][M];   //f[i][j]:区间[i,i+2^j-1]内的最值

void Init()
{
    for(int i=0;i<M;i++) pow_2[i]=1<<i;
    
    log_2[0]=-1;
    for(int i=1;i<N;i++) log_2[i]=log_2[i/2]+1;
}

void get_ST()
{
    for(int i=1;i<=log_2[n];i++)   //先枚举区间长度
        for(int j=1;j+pow_2[i]-1<=n;j++)
                f[j][i]=max(f[j][i-1],f[j+pow_2[i-1]][i-1]);
}

int query(int l,int r)
{
    int k=log_2[r-l+1];
    return max(f[l][k],f[r-pow_2[k]+1][k]);
}

int main()
{
    Init();
    
    cin>>T;
    while(T--)
    {
        memset(f,0,sizeof f);
        
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&f[i][0]);
        
        get_ST();
        
        scanf("%d",&m);
        int l,r;
        while(m--)
        {
            scanf("%d%d",&l,&r);
            printf("%d\n",query(l,r));
        }
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值