当前位置: 首页 > news >正文

[数据结构]ST表(markdown重制版)

								ST表

一、适用情况

ST 表 能在O(1)O(1)O(1)的时间内查询 满足可重复贡献问题(幂等性)和结合律的区间信息

如:区间最值,区间GCD,区间LCM,区间按位或,区间按位与 等

st[i][j] 表示起点为i ,区间长度为2j2^j2jgcd或者max

也就是a[i,i+(1<<j)-1]内的gcd

二、如何递推?

首先要初始化
遍历每个ist[i][0]赋值成a[i] ,因为j==0表示区间长度为202^020 即为1
也就是起点所对应的元素本身
​​
下标示意图
​​​​​​​​​​在两个相邻的区间内取maxgcd 这两个区间的起点相邻 区间长度相同
都是2j−12^{j-1}2j1 区间合并刚好就是要求的区间

三、如何查询?

query(L, R) 的具体步骤:

  1. 计算区间的长度
    首先,计算区间的长度:
    len = R - L + 1

  2. 找到最大可以覆盖该区间长度的 2k2^k2k
    对于给定区间的长度,我们需要找到一个最大的 k,使得 2^k 小于等于该长度 len
    这个 k 可以通过 log2(len)log_2(len)log2(len)来计算,因为log2(len)log_2(len)log2(len)给出的是最大的 k,使得 2k≤len2^k \le len2klenlog2(len)log_2(len)log2(len)int接收 后面的小数就被截断
    所以一个2k2^k2k无法完全覆盖len,所以将目标区间拆成两个部分(有重叠)

  3. 将区间 [L, R] 分为两个子区间
    通过选定的 k 值,区间 [L, R] 被分成两部分:
    计算int k=log(r-l+1);

  • 第一个子区间:从 LL + 2^k - 1(即,长度为 2^k
  • 第二个子区间:从 R - 2^k + 1R(也是长度为 2^k


子区间重叠对于max, gcd 等操作毫无影响,这就是st表巧妙的一点
通过k 将[l,r]分成两个可能有重叠的子区间 重叠并不会造成影响

四、代码实现

1. 模板

#include <iostream>
#include <cmath>
using namespace std;const int MAX_N = 1000;  // 假设最大元素个数为1000
const int MAX_LOG = 10;   // 假设 n 最多为 2^10 = 1024int arr[MAX_N];   // 存储原始数组
int st[MAX_N][MAX_LOG];  // ST表,st[i][j] 表示区间 [i, i + 2^j - 1] 的最小值
// 构建 ST 表
void build(int n) {for (int i = 0; i < n; i++) {st[i][0] = arr[i];}for (int j = 1; (1 << j) <= n; j++)for (int i = 0; i + (1 << j) - 1 < n; i++)st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}int query(int L, int R) {int len = R - L + 1;int k = __lg(len);return min(st[L][k], st[R - (1 << k) + 1][k]);
}int main() {int n;cin >> n;for (int i = 0; i < n; i++) cin >> arr[i];build(n);int L, R;cin >> L >> R;cout << "区间 [" << L << ", " << R << "] 的最小值为: " << query(L, R) << endl;
}

1.__lg()取以2为底的对数 向下取整 即 n 在二进制中的最高位 1 的位置(从 0 开始计数)时间复杂度O(1)O(1)O(1)

2.build中 外层循环 先遍历第二维j 就是按区间长度从小到大遍历 (1<<j)<=n

内层循环遍历起点i 循环结束条件是区间终点不越界 也就是i+(1<<j)-1<n 要记得区间终点-1 计算起点+1

2. Codeforces Round 991 (Div. 3)F题 ​

const int N = 2e5 + 10;
int a[N], n, q, st[N][20];
// 最大的log(n),最大n=2e5所以log(2e5)大约为 18,取 20 以保证覆盖范围
void build() {for (int i = 0; i < n - 1; i++) st[i][0] = abs(a[i] - a[i + 1]);for (int j = 1; (1 << j) <= n; j++)for (int i = 0; i + (1 << j) - 1 < n - 1; i++)st[i][j] = gcd(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}
int query(int l, int r) {int k = __lg(r - l + 1);return gcd(st[l][k], st[r - (1 << k) + 1][k]);
}
void solve() {cin >> n >> q;for (int i = 0; i < n; i++) cin >> a[i];// memset(st,1,sizeof st);for (int i = 0; i < n - 1; i++)for (int j = 0; j < 20; j++) st[i][j] = 1;build();while (q--) {int l, r;cin >> l >> r;if (l == r) {cout << 0 << ' ';continue;}l--, r -= 2;cout << query(l, r) << ' ';}cout << endl;
}

1.每个数mod m同余 相当于每个数的差都是m的倍数 只需要求区间的gcd就行 所以用st表

2.注意LOGN取20就行

3.不要用memset初始化 比两层循环初始化为1慢了20倍 直接超时了 memset不能填充1 它实际填充的不是 1,而是 0x01010101 = 16843009(对 int 来说)

4.l--,r-=2 首先l, r都-1 是因为题目给的是1-based 我们把数组改成了0-based 其次 r再-1 是因为差分数组之后 原数组大小-1 查询的右区间也-1 找个例子模拟一下就行

http://www.dtcms.com/a/407592.html

相关文章:

  • 深圳网站建设saote网站建设项目登记表
  • STL 基础概念
  • dw做旅游网站毕业设计模板下载适合代码新手做的网站
  • 为什么公司网站打不开网页链接制作生成
  • 网站制作模板过程开发app的网站有哪些
  • 原来两个std::vector可以直接进行相等判断的吗?
  • 电子工程基础原理与应用指南(初学者版)电子工程入门:三极管与MOS管全解析
  • Rust/C/C++ 混合构建 - 用Bazel构建Rust与C
  • 微信公众号的网站长沙景点视频
  • 东莞网站建设qq群网站建设与维护项目六
  • ProxySQL:实现MySQL8.0主从同步与读写分离
  • 制作网站需要学什么软件灵璧做网站的公司
  • 基于FPGA的HDB3编解码(verilog语言)
  • 北京个人网站备案wordpress 同步公众号
  • python 做网站多用户商城网站建设
  • 如何学做网站外包做网站与网页有什么区别
  • 视觉SLMA工具
  • 初识nextjs
  • 海口手机版网站建设管理网站建设
  • 定西市网站建设咨询python做网站步骤
  • 万网官方网站深圳设计功能网站
  • 网站添加什么东西才能和用户体验软文广告是什么意思
  • html网站首页设计正常做网站多少钱
  • 神经网络(①MNIST 手写数字识别)
  • 汕头智能模板建站网站建设需求分析报告
  • HAProxy实验步骤
  • 语义分割Semantic segmentation
  • strace / ltrace / ptrace / ftrace
  • 免费自建网站步骤天津工程造价信息网
  • AI+内容工作坊破解企业内容生产与AI落地难题