ST算法

Posted 57xmz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ST算法相关的知识,希望对你有一定的参考价值。

题目背景

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

请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)O(1)。若使用更高时间复杂度算法不保证能通过。

如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch==‘-‘) f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

题目描述

给定一个长度为 NN 的数列,和 MM 次询问,求出每一次询问的区间内数字的最大值。

输入格式

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

第二行包含 NN 个整数(记为 a_iai?),依次表示数列的第 ii 项。

接下来 MM行,每行包含两个整数 l_i, r_ili?,ri?,表示查询的区间为 [ l_i, r_i][li?,ri?]

输出格式

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

输入输出样例

输入 #1
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
输出 #1
9
9
7
7
9
8
7
9

说明/提示

对于30%的数据,满足: 1 leq N, M leq 101N,M10

对于70%的数据,满足: 1 leq N, M leq {10}^51N,M105

对于100%的数据,满足: 1 leq N leq {10}^5, 1 leq M leq 2 imes {10}^6, a_i in [0, {10}^9], 1 leq l_i leq r_i leq N1N105,1M2×106,ai?[0,109],1li?ri?N

 


思路:

首先搞一个二维数组f[i][j]来记录从i这个点往后区间长度为2^j的最大值。ST是很明显的倍增,倍增实质上是dp,那么就要有边界条件和状态转移方程。先找状态转移方程:在区间l,r之间找一个值k,使这个值k满足[l,l+2^k]并[r-2^k+1,r]这两个区间能够覆盖[l,r],由于最大值不用管区间重不重复,一个100放这里你来一千个1也不管用,所以无所谓重不重复,只要能覆盖就行。那么状态转移方程就自然而然的想到:f[i][j]=max(f[i][i+2^k],f[r-2^k][k])。现在状态转移方程有了,找边界条件即可。发现当j=0时就是它自己。

 

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int f[100100][20];
int a[100010];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch==-) f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}//快读,不用快读会T(题目里都告诉你了,不用白不用) 
void ST_prework()
{
    for(int i=1;i<=n;i++) 
        f[i][0]=a[i];//边界条件,由于f[i][j]的含义是在区间[i,i+2^j-1]中的最大值,那么如果将j=0代入得到的结果就是在区间[i,i]中,这显然就只有它自己一个数,所以可以设为边界条件 
    int t=log(n)/log(2);//对数换底公式,值为log2(n),是能取到的最大值,因为你不能超过n个数 
    for(int j=1;j<=t;j++)
        for(int i=1;i<=n-(1<<j)+1;i++)//因为区间长度为 2^j,所以最多取到这个值 
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);//状态转移方程(倍增实际上就是dp) 
}
int ST_query(int l,int r)
{
    int k=log(r-l+1)/log(2);//保证k取这个值能够覆盖整个区间 
    return max(f[l][k],f[r-(1<<k)+1][k]);//因为一个区间里是2^k个数,所以是f[r-(1<<k)+1][k],个数自己数一下就可以 
}
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    ST_prework();
    while(m--)
    {
        int l,r;
        l=read();
        r=read();
        printf("%d
",ST_query(l,r));
    }
    return 0;
}

 

 

题目背景

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

请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)O(1)。若使用更高时间复杂度算法不保证能通过。

如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch==‘-‘) f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

题目描述

给定一个长度为 NN 的数列,和 MM 次询问,求出每一次询问的区间内数字的最大值。

输入格式

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

第二行包含 NN 个整数(记为 a_iai?),依次表示数列的第 ii 项。

接下来 MM行,每行包含两个整数 l_i, r_ili?,ri?,表示查询的区间为 [ l_i, r_i][li?,ri?]

输出格式

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

输入输出样例

输入 #1
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
输出 #1
9
9
7
7
9
8
7
9

说明/提示

对于30%的数据,满足: 1 leq N, M leq 101N,M10

对于70%的数据,满足: 1 leq N, M leq {10}^51N,M105

对于100%的数据,满足: 1 leq N leq {10}^5, 1 leq M leq 2 imes {10}^6, a_i in [0, {10}^9], 1 leq l_i leq r_i leq N1N105,1M2×106,ai?[0,109],1li?ri?N

以上是关于ST算法的主要内容,如果未能解决你的问题,请参考以下文章

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

片段(Java) | 机试题+算法思路+考点+代码解析 2023

RMQ问题 ST算法

[新增ST-001片段]全程字幕-20套UML+Enterprise Architect建模示范视频

LCA 算法ST表

ST算法