首页 > 编程语言 >st 算法学习笔记

st 算法学习笔记

时间:2024-03-04 15:14:13浏览次数:21  
标签:int 笔记 st 算法 区间 查询 dp

前言

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

  • 1.倍增思想。

  • 2.动态规划思想。

  • 3.乘方位运算实现

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

什么是 \(st\) 算法?

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

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

\(st\) 算法具体实现

接下来给大家讲解如何实现 \(st\) 算法。

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

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

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

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

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

则此时,我们就可以先预处理出所有 \(dp[i][j]\)。时间复杂度也就是 \(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]\),且这两个区间可以不刚好包含,两个区间可以存在交集。

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

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

\(st\) 算法的一些扩展

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

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

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

洛谷 P1890 gcd区间

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

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

我们只需要把 RMQ 问题中的 \(max\) 改为 \(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;
}

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

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

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

后记

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

标签:int,笔记,st,算法,区间,查询,dp
From: https://www.cnblogs.com/SFsaltyfish/p/18051837

相关文章

  • day54 动态规划part11 代码随想录算法训练营 123. 买卖股票的最佳时机 III
    题目:123.买卖股票的最佳时机III我的感悟:困难像弹簧,你强他就弱,你弱它就强!理解难点:5个状态,听课笔记:我的代码:classSolution:defmaxProfit(self,prices:List[int])->int:iflen(prices)==1:return0#5种状态#dp[i......
  • AtCoder Beginner Contest 313
    \[\large\text{Round2:AtCoderBeginnerContest313(VP)}\]一言:当我拔出第二把剑时,就是为了我所爱之人——刀剑神域这场比赛真的是大败而归,只A了\(A,B,C,E\)。。。虽然但是,\(F,G\)确实不可做,但是\(D\)题还是有点可惜了。\(\text{D:OddorEven}\)感觉考场......
  • 关于SAP-APP机器-R3trans -d报错-R3trans: /lib64/libstdc++.so.6: version `GLIBCXX_
    在SAP-应用-APP-机器上执行如下命令报错awpxxx03:prdadm270>R3trans-dR3trans:/lib64/libstdc++.so.6:version`GLIBCXX_3.4.26'notfound(requiredbyR3trans) 其实之前,使用过一种方法解决这个问题,可以参考笔者另一篇文章《关于Redhat-Linux中-compat-sap-c++的说......
  • 有一个子组件DataList,然后在父组件中引入,并在父组件引入中的DataList标签上设置style
    有一个子组件DataList,然后在父组件中引入,并在父组件引入中的DataList标签上设置style样式,能生效吗?在React中,父组件可以通过props将样式传递给子组件,并在子组件内部应用这些样式。但直接在父组件引用子组件的地方设置style属性通常不会生效,因为React的JSX语法并不支持这种写法。......
  • 【个人前端笔记】web性能优化:连接复用
    一、连接复用keep-alive当我们去连接www.baidu.com的时候,会经历以下过程(没有连接复用)连接过程:发起TCP连接---->请求资源----->下载资源---->关闭TCP连接---->再次发起TCP连接.....如果有多个资源需要请求,我们就要发起tcp然后关闭tcp连接,然后再发起和关闭如果可以发起一次tcp......
  • 黑马程序员JavaWeb学习笔记-过滤器
    过滤器--Filter过滤器Filter快速入门Filter拦截路径过滤器链Filter——流程importcom.alibaba.fastjson.JSONObject;importcom.itheima.pojo.Result;importlombok.extern.slf4j.Slf4j;importorg.springframework.util.StringUtils;importjavax.servlet.*;im......
  • 黑马程序员JavaWeb学习笔记-拦截器
    拦截器--Interceptor--快速入门@Component注解交给ioc容器管理--注册配置拦截器@Configuration注解用来标识当前是Spring当中的一个配置类//Interceptor拦截所有("/**")//Filter拦截所有("/*")//WebConfig需要在包下新建一个config包与controller同级//.excl......
  • 黑马程序员JavaWeb学习笔记-文件上传
    文件上传https://www.bilibili.com/video/BV1m84y1w7Tb/?p=150&spm_id_from=pageDriver&vd_source=62f4901d4d947272c439194b87ec6698当报错500时,服务端出现错误,因为默认最大为1M在application.properties里面修改文件上传的几个函数本地存储Controller层的代码import......
  • 【个人前端笔记】Event loop和微任务与宏任务
    一、EventloopEventloop是指在node.js的事件循环,不是在浏览器中二、Eventloopd各个阶段┌───────────────────────┐┌─>│timers│timers阶段:这个阶段执行setTimeout和setInterval的回调函数。│└───────......
  • 黑马程序员JavaWeb学习笔记-登陆login
    登陆loginlogin是登陆业务方法,mapper接口是持久层,用来操作数据库的,用业务方法名不合适三层架构PostMan测试登陆校验http协议是无状态的,下次请求不会携带上次请求的数据,两次请求是独立的Cookie前后端分离项目中前端页面和后段接口部署在不同的服务器上,所以他们的协议......