【6】ST表学习笔记
前言
学习ST表,主要是倍增思想,可以理解为倍增优化后的DP。写在这里,一方面方便自己以后复习,另一方面给其他人参考。
UPD on 2023/3/21 :修改了格式,使格式与其他的学习笔记统一。
倍增
倍增引入
与其说倍增是一种算法,不如说倍增是一种思想。
倍增的时间复杂度和二分是一样的,都是
我们进行递推时,如果状态空间很大,通常线性递推无法满足时空复杂度的要求,那可以通过成倍增加,以
对于
i=(1<<(n-1))
注意:由于位运算优先级分布不均匀,使用时一般搭配括号。
倍增的经典应用:快速幂,ST表,LCA,后缀数组。
ST表
ST表可以用来解决RMQ(区间最值询问)问题,是一个静态算法。这里求解的是区间最小值问题。当询问数量较多,达到
主要思想
预处理
设状态
易得
状态转移方程如下:
由状态定义得,其中
由于相交或相接的区间可以合并,故把
注意:由于
预处理完整代码如下:
for(int i=1;i<=n;i++)
f[i][0]=a[i];
for(int j=1;j<=MAXM;j++)
for(int i=1;i+(1<<(j-1))<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
时间复杂度:
询问
对于询问区间
然后可得
由状态定义得,其中
很显然,这两个区间是相交或相接的,故可以合并最小值,求出答案。
回答询问完整代码如下:
int k=(int)log2(t-s+1);
printf("%d\n",max(f[s][k],f[t-(1<<k)+1][k]));
时间复杂度:
ST表例题
例题
P3865 【模板】ST 表
ST表模板题,不多赘述。
#include <bits/stdc++.h>
#define MAX 600000
using namespace std;
int n,m,s,t,a[2000000],f[100050][21];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
f[i][0]=a[i];
for(int j=1;j<=20;j++)
for(int i=1;i+(1<<(j-1))<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++)
{
s=read();t=read();
int k=(int)log2(t-s+1);
printf("%d\n",max(f[s][k],f[t-(1<<k)+1][k]));
}
return 0;
}
例题
P2880 [USACO07JAN] Balanced Lineup G
很显然,只需要改变
#include <bits/stdc++.h>
using namespace std;
int n,m,s,t,a[2000000],f[100050][21],f2[100050][21];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
f[i][0]=a[i],f2[i][0]=a[i];
for(int j=1;j<=20;j++)
for(int i=1;i+(1<<(j-1))<=n;i++)
{
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
f2[i][j]=min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);
}
for(int i=1;i<=m;i++)
{
s=read();t=read();
int k=(int)log2(t-s+1);
printf("%d\n",max(f[s][k],f[t-(1<<k)+1][k])-min(f2[s][k],f2[t-(1<<k)+1][k]));
}
return 0;
}
后记
RMQ问题还可以用线段树来解决,这里不多赘述。
教练推荐的一篇博客:
倍增与ST算法 --算法竞赛专题解析(28)