st 算法学习笔记

前言

该文章同步于 c n b l o g s cnblogs cnblogs 发布。

在看这篇文章之前,请先自行了解以下几项东西:

  • 1.倍增思想。

  • 2.动态规划思想。

  • 3.乘方位运算实现

如有错误,欢迎各位 dalao 批评指出。

什么是 s t st st 算法?

st 算法是一种解决 RMQ 问题的算法。RMQRange Minimum/Maximum Query,即区间最大最小值查询。

该算法采用了 动态规划和倍增 的思想,预处理 O ( n l o g n ) O(nlogn) O(nlogn),查询 O ( 1 ) O(1) O(1),适用于一些查询较多的题目。

s t st st 算法具体实现

接下来给大家讲解如何实现 s t st st 算法。

我们假定是要求出 a a a 数组上的区间最大值,

我们定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从 i i i 开始,长度为 2 j 2^j 2j 的区间的最大值。

则对于初始值, d p [ i ] [ 0 ] = a [ i ] dp[i][0]=a[i] dp[i][0]=a[i],这个比较容易想到,即从 i i i 开始,长度为 1 1 1 的区间就是它本身,所以是 a [ i ] a[i] a[i]

接着是状态转移。显然,对于一个长度为 2 j 2^j 2j 的区间,我们可以把它划分为两个长度为 2 j − 1 2^{j-1} 2j1 的区间,然后再求最大值。则状态转移为: d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i + ( 1 < < ( j − 1 ) ) ] [ j − 1 ] ) dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]) dp[i][j]=max(dp[i][j1],dp[i+(1<<(j1))][j1]),其中 1 < < ( j − 1 ) 1<<(j-1) 1<<(j1),就表示 2 j − 1 2^{j-1} 2j1

但是,当我们状态转移在枚举循环时,我们需要注意的是,我们要先枚举区间长度越小的在枚举区间长度越大的,所以 j j j 应该放在外层循环,而 i i i 就应该放在内层循环。

则此时,我们就可以先预处理出所有 d p [ i ] [ j ] dp[i][j] dp[i][j]。时间复杂度也就是 O ( n l o g n ) O(nlogn) O(nlogn)

核心代码:

void init()//预处理函数
{
	for(int i=1;i<=n;++i)
	dp[i][0]=a[i];//初始值
	for(int j=1;(1<<j)<=n;++j)//第二维j,注意 2^j 要小于n
	{
		for(int i=1;i+(1<<j)-1<=n;++i)//枚举i时要保证从i开始,有一个长度为 2^j 的区间时,不能超出 n。
		{
			dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)]][j-1]);//状态转移
		}
	}
}

最后就是如何查询的问题。

假设我们要查询的是 [ l , r ] [l,r] [l,r] 这个区间的区间最大值。我们可以发现,我们查询最大值的话,我们可以将 [ l , r ] [l,r] [l,r] 分成两个区间来查询,并且这两个区间总体合起来要包含 [ l , r ] [l,r] [l,r],且这两个区间可以不刚好包含,两个区间可以存在交集。

此时,我们可以定义一个数字 k k k,表示 log ⁡ 2 r − l + 1 \log_2^{r-l+1} log2rl+1(向下取整) ,则我们就可以根据 k k k,来求出我们要寻找的 [ l , r ] [l,r] [l,r] 的区间最大值,即求出 m a x ( d p [ l ] [ k ] , d p [ r − ( 1 < < k ) + 1 ] [ k ] ) max(dp[l][k],dp[r-(1<<k)+1][k]) max(dp[l][k],dp[r(1<<k)+1][k]) 。这个查询的正确性也是十分明显,因为两个区间长度都是 2 k 2^k 2k,并且 2 k + 2 k = 2 k + 1 > ( r − l + 1 ) 2^k+2^k=2^{k+1}>(r-l+1) 2k+2k=2k+1>(rl+1),所以这两个区间一定可以完全覆盖 [ l , r ] [l,r] [l,r]

查询核心代码:

int rmq(int l,int r)
{
	int k=log2(r-l+1);
	return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

可以发现, s t st st 算法是无法完成修改操作的,即使要去完成也效率过慢。但是对于只查询的题目来说, s t st st 算法一定是当之无愧的神!

s t st st 算法的一些扩展

s t st st 算法是解决 RMQ 问题的,但是这种算法的思想我们却可以继续延伸下去。

s t st st 算法之所以不能解决区间和是因为他在查询时,取 m a x max max 两个区间可能存在交集,也就是可能会重复计算。那么是不是可以重复计算的无修改的问题都可以运用 s t st st 算法的思想解决呢?of course!

为了让大家更好理解,我这里先抛给大家一道例题,相信你做出来一定可以领会到 s t st st 算法思想的精髓所在。

洛谷 P1890 gcd区间

显而易见,这个题目就是求区间 GCD ,并且我们可与发现,GCD 我们是可以重复计算的。举个例子, g c d ( 10 , 5 ) = g c d ( 10 , 10 , 5 ) gcd(10,5)=gcd(10,10,5) gcd(10,5)=gcd(10,10,5)

既然可以重复计算,那就肯定可以用 s t st st 算法的思想进行求解了!

我们只需要把 RMQ 问题中的 m a x max max 改为 g c d gcd gcd 就可以了。(代码中为了方便,我直接调用了 c++ 的内置函数 __gcd

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[500005];
int dp[500005][35];
void init()
{
	for(int j=1;(1<<j)<=n;++j)
	{
		for(int i=1;i+(1<<j)-1<=n;++i)
		{
			dp[i][j]=__gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);//直接求 i~i+2^j-1 这个区间的gcd
		}
	}
}
int rmq(int l,int r)
{
	int k=log2(r-l+1);
	return __gcd(dp[l][k],dp[r-(1<<k)+1][k]);//我们只需要把 max 改为 __gcd 即可。
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]),dp[i][0]=a[i];
	init();
	while(m--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",rmq(l,r));
	}
	return 0;
}

除了在算法思想上的扩展,对于算法本身,我们也可以进行扩展。

例如 s t st st 算法本身其实是可以有二维形式的,例题:洛谷 P2216 [HAOI2007]理想的正方形

这个题目的话思路还是比较清晰的,至于具体代码实现请读者自己实践,如果实在做不出来就看一看题解吧。

后记

今天的算法就讲到这里,希望大家能有所收获,如有不懂的地方,也可以私信我。

my QQ number:1262264033

我们下次再见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值