ST 表

定义

ST 表是用于解决 可重复贡献问题 的数据结构,通俗来说,一般可以解决区间查询问题。

区间最值和 \(gcd\)

我们以最大值为例,然后可以再推广到最小值和区间 \(gcd\)

  • 首先你应该知道的是,ST表是利用倍增思想来缩短时间的。而倍增就体现在他数组的定义中:对于$f[i][j]$,指的是在序列的第i项,向后$2^j$个元素所包含序列间的最大值
  • 对于$i=1$,我们可以画出这么一个图,其下标即为$j$:

    那么对于当前$i$转移其实很明显了,我们可以直接考虑将两个小区间的答案合并,即为这个大区间的值;如图中$f[1][2]$即可由$max(f[1][1],f[3][1])$转移来。

    $$f[i][j]=max(f[i][j-1],f[i+2^{j-1}][j-1])$$

    其中$2^{j-1}$也可写为$(1<<(j-1))$,这里位运算会更方便也会更快。

    这个式子告诉我们,$ST$ 表类似于区间 $dp$,是由两个小区间合并上来的。所以应该先枚举区间长度l(这里即为$j$),再枚举$i$.

    1. 然后一个问题应运而生了:我们这个转移方程有没有边界呢?

    不妨来看一下$i=6$的图:

    可以看出在$i=6$时,$j=3$的范围是$[6,13(6+2^3)]$,已经超出了我们数据的范畴。所以当$j=3$时,$i$只能取到$[1,5(12-2^3+1)]$

    由上例再根据转移方程,不难看出当$j$确定时,$i$的范围受限在$[1,n-2^j+1]$。

    我们现在来求红色标记区间$[L,R]$的最值。如果要最大化利用ST表,仍应该考虑类似处理ST表的方法,将该区间分成 两个ST表可直接维护的小区间,然后二者求最值即可。

    • 那对于起始点,我们找一段ST表在该区间内可覆盖的,最大的子区间,由数学语言可描述为:

      $(L+2^k-1<=R) \Leftrightarrow (k<=lg[R-L+1])$ 那我们直接取等,令$j=k$即可~

    于是对于起始点点在ST表里的取值即为:$f[L][k]$

    • 对于终止点,我们反向找一个与起始点要求相同的子区间,由于对称性,此时k仍为起始点求得的$k=lg[R-L+1]$

    但是我们应该如何确定该子区间的起点呢?由于子区间长度为$2^k$,设起点在$D$处,则满足:

    $(D+2^k-1=R) \Leftrightarrow (D=R-2^k+1)$

    于是对于终止点在ST表里的取值即为:$f[D][k]$,可证明这样一定可以覆盖整个区间。

    综上,对于区间$[L,R]$求其最值,不难发现答案即为:

    $$\max(f[L][k],f[R-(1<<k)+1][k])$$

    同理,求 \(min\) 和 \(gcd\) 的过程和以上过程是一样的,在这里附上 P3865 的代码

    模板题代码

    给定一个长度为 \(N\) 的数列,和 $ M $ 次询问,求出每一次询问的区间内数字的最大值。

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

    第二行包含 \(N\) 个整数(记为 \(a_i\)),依次表示数列的第 \(i\) 项。

    接下来 \(M\) 行,每行包含两个整数 \(l_i,r_i\),表示查询的区间为 \([l_i,r_i]\)。

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

    1. #include <bits/stdc++.h>
    2. #define rint register int
    3. #define endl '\n'
    4. using namespace std;
    5. const int N = 1e6 + 5;
    6. const int M = 2e1 + 1;
    7. int n, m;
    8. int gcd_[N][M];
    9. int maxx[N][M];
    10. int minn[N][M];
    11. int gcd(int a, int b)
    12. {
    13. if (!b) return a;
    14. return gcd(b, a % b);
    15. }
    16. int query_gcd(int l, int r, int *a)
    17. {
    18. int k = log2(r - l + 1);
    19. return gcd(a[l * M + k], a[(r - (1 << k) + 1) * M + k]);
    20. }
    21. int query_max(int l, int r, int *a)
    22. {
    23. int k = log2(r - l + 1);
    24. return max(a[l * M + k], a[(r - (1 << k) + 1) * M + k]);
    25. }
    26. int query_min(int l, int r, int *a)
    27. {
    28. int k = log2(r - l + 1);
    29. return min(a[l * M + k], a[(r - (1 << k) + 1) * M + k]);
    30. }
    31. signed main()
    32. {
    33. cin >> n >> m;
    34. for (rint i = 1; i <= n; i++)
    35. {
    36. int k;
    37. cin >> k;
    38. maxx[i][0] = k;
    39. minn[i][0] = k;
    40. gcd_[i][0] = k;
    41. }
    42. for (rint j = 1; j <= M; j++)
    43. {
    44. for (rint i = 1; i + (1 << j) - 1 <= n; i++)
    45. {
    46. int k = i + (1 << (j - 1));
    47. maxx[i][j] = max(maxx[i][j - 1], maxx[k][j - 1]);
    48. minn[i][j] = min(minn[i][j - 1], minn[k][j - 1]);
    49. gcd_[i][j] = gcd(gcd_[i][j - 1], gcd_[k][j - 1]);
    50. }
    51. }
    52. for (rint i = 1; i <= m; i++)
    53. {
    54. int l, r;
    55. cin >> l >> r;
    56. cout << query_max(l, r, (int *)maxx) << endl;
    57. //cout << query_min(l, r, (int *)minn) << endl;
    58. //cout << query_gcd(l, r, (int *)gcd_) << endl;
    59. }
    60. return 0;
    61. }

    [NOI2010] 超级钢琴

    有 $n$ 个音符,编号为 $1$ 至 $n$ 。第 $i$ 个音符的美妙度为 $A_i$ 。

    我们要找到 $k$ 段超级和弦组成的乐曲,每段连续的音符的个数 $x$ 满足 $L\leq x\leq R$ ,求乐曲美妙度的最大值。

    首先,对于一段区间在左端点固定的情况下它的取值范围为 $sum[i + k]-sum[i - 1]\sim sum[i + k]-sum[i-1]$ 之间,$k\subseteq [l, r]$.所以,我们只需让前面一项最大即可。

    ST 表维护一个前缀最大值,让后,每次取出使区间和最大的端点,再用前缀和计算区间和。这里用优先队列可以做到这点,同时用类似 ST 表的方法维护一个区间和最大的端点。

    再考虑,由于不能出现两个相同的区间,所以取完一个区间后,设 $now$ 为选择的节点,它会分裂成两个区间。即 $l\sim now - 1$ 和 $now + 1\sim r$。判断是否合法之后加入优先队列,取 $k$ 次,就是最大值。

    1. #include <bits/stdc++.h>
    2. #define rint register int
    3. #define int long long
    4. #define endl '\n'
    5. using namespace std;
    6. const int N = 5e5 + 5;
    7. const int M = 2e1 + 1;
    8. int n, m, L, R;
    9. int a[N], s[N];
    10. int f[N][M];
    11. int ans;
    12. struct node
    13. {
    14. int l, r, p, q;
    15. //p 是左端点, l 和 r 是右端点的范围, q 是当前解的右端点的位置
    16. bool operator < (const node &x) const
    17. {
    18. return s[x.q] - s[x.p] > s[q] - s[p];
    19. }
    20. };
    21. priority_queue<node> q;
    22. int max(int a, int b)
    23. {
    24. return a > b ? a : b;
    25. }
    26. int min(int x, int y)
    27. {
    28. return s[x] < s[y] ? x : y;
    29. }
    30. int query_min(int l, int r, int *a)
    31. {
    32. int k = log2(r - l + 1);
    33. return min(a[l * M + k], a[(r - (1 << k) + 1) * M + k]);
    34. }
    35. signed main()
    36. {
    37. cin >> n >> m >> L >>R;
    38. for (rint i = 1; i <= n; i++)
    39. {
    40. cin >> a[i];
    41. }
    42. for (rint i = 1; i <= n; i++)
    43. {
    44. s[i] = s[i - 1] + a[i];
    45. f[i][0] = i;
    46. }
    47. for (rint j = 1; j <= M; j++)
    48. {
    49. for (rint i = 0; i + (1 << j) - 1 <= n; i++)
    50. //如果你在前面找最小值, ST 表要从 0 开始初始化
    51. {
    52. int k = i + (1 << (j - 1));
    53. f[i][j] = min(f[i][j - 1], f[k][j - 1]);
    54. }
    55. }
    56. for (rint i = L; i <= n; i++)
    57. {
    58. int r = i - L;
    59. int l = max(0, i - R);
    60. q.push({l, r, query_min(l, r, (int *)f), i});
    61. }
    62. for (rint i = 1; i <= m; i++)
    63. {
    64. node k = q.top();
    65. q.pop();
    66. ans += s[k.q] - s[k.p];
    67. int l = k.l;
    68. int r = k.p - 1;
    69. if (l <= r)
    70. {
    71. q.push({l, r, query_min(l, r, (int *)f), k.q});
    72. }
    73. l = k.p + 1;
    74. r = k.r;
    75. if (l <= r)
    76. {
    77. q.push({l, r, query_min(l, r, (int *)f), k.q});
    78. }
    79. }
    80. cout << ans << endl;
    81. return 0;
    82. }

    ST 表的更多相关文章

    1. POJ3693 Maximum repetition substring [后缀数组 ST表]

      Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9458   Acc ...

    2. 【BZOJ-2006】超级钢琴 ST表 + 堆 (一类经典问题)

      2006: [NOI2010]超级钢琴 Time Limit: 20 Sec  Memory Limit: 552 MBSubmit: 2473  Solved: 1211[Submit][Statu ...

    3. 【BZOJ-3956】Count ST表 + 单调栈

      3956: Count Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 173  Solved: 99[Submit][Status][Discuss] ...

    4. 【BZOJ-4569】萌萌哒 ST表 + 并查集

      4569: [Scoi2016]萌萌哒 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 459  Solved: 209[Submit][Status] ...

    5. 【BZOJ-4310】跳蚤 后缀数组 + ST表 + 二分

      4310: 跳蚤 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 180  Solved: 83[Submit][Status][Discuss] De ...

    6. HDU5726 GCD(二分 + ST表)

      题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5726 Description Give you a sequence of N(N≤100, ...

    7. Hdu 5289-Assignment 贪心,ST表

      题目: http://acm.hdu.edu.cn/showproblem.php?pid=5289 Assignment Time Limit: 4000/2000 MS (Java/Others) ...

    8. Bzoj 2006: [NOI2010]超级钢琴 堆,ST表

      2006: [NOI2010]超级钢琴 Time Limit: 20 Sec  Memory Limit: 552 MBSubmit: 2222  Solved: 1082[Submit][Statu ...

    9. ST表poj3264

        /* ST表多次查询区间最小值 设 g[j][i] 表示从第 i 个数到第 i + 2 ^ j - 1 个数之间的最小值 类似DP的说 ans[i][j]=min (ans[i][mid],ans ...

    10. COJ 1003 WZJ的数据结构(三)ST表

      WZJ的数据结构(三) 难度级别:B: 运行时间限制:3000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 请你设计一个数据结构,完成以下功能: 给定一个大小为N的 ...

    随机推荐

    1. [mysql]安全加固

      前言 因等保安全的要求,需要对MySQL用户密码和登录策略进行安全加固,以满足以下需求: 密码至少8位,包含大小写字母.数字和特殊字符. 当密码登录失败一定次数后锁定账户. 密码90天过期 本文使用的 ...

    2. 3.你所不知道的go语言控制语句——Leetcode习题69

      目录 本篇前瞻 Leetcode习题9 题目描述 代码编写 控制结构 顺序结构(Sequence) 声明和赋值 多返回值赋值 运算符 算术运算符 位运算符 逻辑运算 分支结构 if 语句 switch ...

    3. 你能看到这个汉字么“  ” ?关于Unicode的私人使用区(PUA) 和浏览器端显示处理

      如果你现在使用的是chrome查看那么你是看不到我标题中的汉字的,显示为一个小方框,但是你使用edge查看的话,这个字就能正常的显示出来,不信你试试! 本故事源于我在做数据过程中遇到Unicode编码 ...

    4. shopee根据ID取商品详情 API

      ​ item_get-根据ID取商品详情  注册开通 shopee.item_get 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中) secr ...

    5. 架构师必会之-DBA级问题的数据库底层设计思想

      大家好,我是sulny_ann,这期想跟大家分享一下我之前在面试里面问过比较难的数据库相关的问题. 大家经常也在调侃后端好像就是技术数据库的增删改查,所以作为后端开发,你对应数据库这一块掌握的怎么样, ...

    6. iOS发送探针日志到日志系统的简单实现

      通过参考Testin的SDK实现方式,我们大致可以确定他们背后的实现方式: 首先,通过加载Testin的SDK,然后收集各种七七八八的数据,再通过socket发送数据到云端. 云端我们已经有了,就是h ...

    7. 2.9 PE结构:重建导入表结构

      脱壳修复是指在进行加壳保护后的二进制程序脱壳操作后,由于加壳操作的不同,有些程序的导入表可能会受到影响,导致脱壳后程序无法正常运行.因此,需要进行修复操作,将脱壳前的导入表覆盖到脱壳后的程序中,以使程 ...

    8. Linux虚拟机安装及下载

      centos 7操作系统下载及安装步骤 (仅供参考) 下载: 1.打开如下网站:先下载镜像文件 ping:https://www.centos.org/download/ 2.进入到如下界面 3.然后 ...

    9. 斜率优化DP 学习笔记

      斜率优化 DP 适用情况 适用于求解最优解(最大.最小)问题. 上凸壳与下凸壳 求解步骤 对于任意状态转义方程,设 \(A_i\),\(B_i\),使状态转移方程转化为 \(f_i = \min(f_ ...

    10. 大模型时代,如何快速开发AI应用

      本文分享自华为云社区 <[云享问答]第3期:大模型时代,如何快速开发AI应用>,作者:华为云社区精选. 大模型快速普及应用的当下,AI浪潮汹涌而至,对于开发者来说,开发一款属于自己的AI应 ...