鱼C论坛

 找回密码
 立即注册
查看: 3348|回复: 28

[技术交流] (C++) ST 表 详解

[复制链接]
发表于 2022-8-26 16:32:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 柿子饼同学 于 2022-8-26 17:26 编辑
ST 表 (C++版)

ST 表 的应用

ST 表 用于静态查找区间最值.

在一个数组中 , 给定一段区间 [L, R] 问你这段区间里的 最大值 / 最小值 是啥.

普通的方法我们当然可以从 L 到 R 遍历一遍 , 但是 , 当元素特别多 ,  L, R  太大 , 询问次数很多时 , 就会非常的慢  

这让我们想到了预处理, 这样不管问什么 , 我们都可以很快回答出来 !

于是 , 就有了 ST表 这么一个东西



原理

首先我们有一个数组 arr[ ] 来装值

我们定义一个二维数组 dp[ ][ ] (是的 , ST 表其实是一种动态规划的思想)

dp[j] 表示 arr 第 i 个元素和它后面 2 ^ j 个元素中的最大值 , 区间是 [i, i + 2^j-1]

为什么这样定义呢 ?

因为每个整数都可以表示成 2^x + 2^y + ... 这样的形式

我们可以通过拼接的方式把目标区间用它 "拼" 成一个完整区间

例如 , 区间 [10, 24] 的最大值可以用   dp[10][3], dp[18][2] 和 dp[22][1] 的最大值  来表示

( dp[10][3] -> [10, 10 + 2^3] -> [10, 18] , 以此类推 )

但是这样有点麻烦 , 有没有更快的方法呢 ?

当然有 !

我们可以直接把它的最大值表示为 dp[10][3] dp[24 - 2^3][3] 的最大值

看图 :

图 1

图 1

由于是取最值 , 所以重叠也没关系 , 只要到整个区间就行了

那我们怎么确定两个小区间的长度呢 ?

就是看大区间最大能 "装下" 2 的几次方 , 假设大区间长度是 l , 则一个小区间长度就是 log2 ( l ) 向下取整

由于 log 函数比较浪费时间 , 所以我们选择预处理 , 反正精度不高 , 我们有如下的递推式 : ( 这个就是个小公式 , 记住就行了 )
  1. log_2 = log_2[i >> 1] + 1;
复制代码
" >> " 是左移符号 , 可以理解为除以 2 的多少次方 , i >> 1 就是 i / 2 的意思

1 << x 就是 2 ^ x 的意思

然后我们就可以读入数据了 , 我们其实不需要单独的数组来存值 , 还记得 dp 的定义吗?

我们把 dp[0] 存入值就好了 , 因为 2 ^ 0 == 1, 区间长为1,  最大值就是它本身

然后 , 就到了 dp 数组的预处理部分 :

由于 dp 第二维是表示 2的次方 , 所以每次增加就会乘 2 , 这个要注意

代码如下 :
  1. for(int j = 1; j <= log_2[n]; j++){ // 第一层枚举 j, 小区间长度
  2.         for(int i = 1; i + (1<<j) - 1 <= n; i++){ // 第二层枚举开始的下标, 注意不要越界
  3.                 dp[j] = max(dp[j-1], dp[i+(1<<(j-1))][j-1]);
  4.         }
  5. }
复制代码

图 2

图 2

原理可以看上图 , 一个大的区间最值等于它分成的两个小区间的最值

以上我们可以写一个初始化函数 :
  1. void init(){
  2.         for(int i = 3; i <= n; i++) log_2 = log_2[i >> 1] + 1;
  3.         for(int j = 1; j <= log_2[n]; j++){
  4.                 for(int i = 1; i + (1 << j) - 1 <= n; i++){
  5.                         dp[j] = max(dp[j-1], dp[i+(1 << (j-1))][j-1]);
  6.                 }
  7.         }
  8. }
复制代码

然后就是我们处理询问的区间 [L, R]

我们先求出 L 到 R 有多少个元素 , 一共是 R-L+1 个

还记得我们的重叠取法吗?

图 3

图 3

根据以上内容我们可以写出代码了吧 :

  1. int query(int l, int r){
  2.         int t = log_2[r-l+1];
  3.         return max(dp[l][t], dp[r-(1<<t)+1][t]);
  4. }
复制代码

好啦, 贴上完整代码:

  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. const int N = 1e5 + 5;
  4. int dp[N][24];
  5. int log_2[N];
  6. int n, q;

  7. void init(){
  8.         for(int i = 2; i <= n; i++){
  9.                 log_2 = log_2[i >> 1] + 1;
  10.         }
  11.         for(int j = 1; j <= log_2[n]; j++){
  12.                 for(int i = 1; i + (1<<j) - 1 <= n; i++){
  13.                         dp[j] = max(dp[j-1], dp[i + (1 << (j-1))][j-1]);
  14.                 }
  15.         }
  16. }

  17. int query(int l, int r){
  18.         int t = log_2[r-l+1];
  19.         return max(dp[l][t], dp[r-(1<<t)+1][t]);
  20. }

  21. int main(){
  22.         ios::sync_with_stdio(0);
  23.         cin.tie(0); cout.tie(0); // 读入优化

  24.         cin >> n >> q;
  25.         for(int i = 1; i <= n; i++) cin >> dp[0];

  26.         init();
  27.         
  28.         int l, r;

  29.         while(q--){
  30.                 cin >> l >> r;
  31.                 cout << query(l, r) << endl;
  32.         }
  33.         
  34.         return 0;
  35. }
复制代码


~感谢观看~





评分

参与人数 4荣誉 +20 鱼币 +20 贡献 +8 收起 理由
tommyyu + 5 + 5 + 3 无条件支持楼主!
小伤口 + 5 + 5 感谢楼主无私奉献!
傻眼貓咪 + 5 + 5 感谢楼主无私奉献!
青出于蓝 + 5 + 5 + 5 鱼C有你更精彩^_^

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-26 16:43:23 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 17:16:51 | 显示全部楼层

回帖奖励 +3 鱼币

高级
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-26 17:28:43 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 17:30:26 | 显示全部楼层

回帖奖励 +3 鱼币

有意思!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-26 17:33:45 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 17:48:35 | 显示全部楼层

回帖奖励 +3 鱼币

感谢分享
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-26 18:00:54 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 19:02:23 | 显示全部楼层

回帖奖励 +3 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-8-26 19:06:22 | 显示全部楼层

回帖奖励 +3 鱼币

专业
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-26 19:25:04 | 显示全部楼层

评分

参与人数 1鱼币 +1 收起 理由
aaron0919 + 1 无条件支持楼主!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-26 19:25:55 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 20:01:06 | 显示全部楼层

回帖奖励 +3 鱼币

受益匪浅
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-26 20:04:16 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-26 20:24:20 | 显示全部楼层

回帖奖励 +3 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-8-26 20:28:39 | 显示全部楼层

回帖奖励 +3 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-26 20:31:23 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-27 08:07:55 | 显示全部楼层

回帖奖励 +3 鱼币

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-8-27 09:14:24 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-27 15:31:47 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-11 16:29

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表