倍增法(ST算法)
倍增法(ST算法)
1.算法描述
算法思想
st算法,适用于RMQ问题(区间最大值),也就是求出一个序列中,数值最大的一项。朴素做法自然是一遍扫描找最大值,但是当题目询问次数很多的时候,就很有可能超时,因此用倍增算法来进行一个优化。
以下是st算法求解RMQ问题的实现:
首先,我们用 f [ i ] [ j ] f[i][j] f[i][j]来表示在序列a中,第i位数字以后数 2 j 2^j 2j个数之中的最大值,那么就可以得到 f [ i ] [ 0 ] = a [ i ] f[i][0]=a[i] f[i][0]=a[i]。
接下来进行一个DP的过程:
for (int j = 1; j <= (log2(n)); j++)
for (int i = 1; i <= n - (1 << j) + 1; i++)
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
2.模板
2.1 预处理
// 预处理
void pre_work() {
for (int i = 1; i <= n; ++i) f[i][0] = a[i]; // 处理长度为1的区间
int t = log(n) / log(2) + 1; // 计算分段数
for (int j = 1; j < t; ++j) // 遍历不同长度的区间
for (int i = 1; i <= n - (1 << j) + 1; ++i) // 左边界遍历
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // 进行dp操作
}
2.2 询问
// 询问区间[l,r]中最值
int ST_query(int l, int r) {
int k = log(r - l + 1) / log(2); // 找到l和r间的一个分界点 t
return max(f[l][k], f[r - (1 << k) + 1][k]); // 返回[l, t]和[t,r]中最大的那个
}
3.典型例题
洛谷 3865【模板】ST表
题意: 给定一个长度为 N 的数列,和 M 次询问,每次给定[l, r],求出每一次询问的区间内数字的最大值。
题解: 模板题
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int a[N], f[N][32];
int n, m;
// 预处理
void pre_work() {
for (int i = 1; i <= n; ++i) f[i][0] = a[i]; // 处理长度为1的区间
int t = log(n) / log(2) + 1; // 计算分段数
for (int j = 1; j < t; ++j) // 遍历不同长度的区间
for (int i = 1; i <= n - (1 << j) + 1; ++i) // 左边界遍历
f[i][j] =
max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // 进行dp操作
}
// 询问区间[l,r]中最值
int ST_query(int l, int r) {
int k = log(r - l + 1) / log(2); // 找到l和r间的一个分界点 t
return max(f[l][k],
f[r - (1 << k) + 1][k]); // 返回[l, t]和[t,r]中最大的那个
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
pre_work();
for (int i = 1; i <= m; ++i) {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", ST_query(a, b));
}
return 0;
}
CF1335F. Robots on a Grid
题意: 给出一个 n×m 的网格,每个格子有颜色,0 黑 1 白,每个格子还有一个方向,表示这个格子上的机器人会向那个方向走,并保证不会走出格子
摆放机器人,它们同时开始运动,在任意时刻不能有两个机器人在同一个格子里。先最大化机器人个数,如果多种方案机器人个数相等,再最大化摆在黑格子里的机器人数量。
n
∗
m
<
=
1
0
6
n * m <= 10^6
n∗m<=106
题解: 思维+倍增。有一个比较显然的结论,如果走了n * m步后,两个机器人不在同一个格子里面,那么这两个机器人永远不会相遇。因为n * m步后,这两个机器人已经进入循环了,此时没有相遇的话,那么分别进入永不相遇的循环。那么对于每一个格子,我都可以知道它走n * m后能够到达的最终的格子是哪个,那么我就可以知道最终的格子是哪个机器人走到的,这个使用倍增就可以做到。如果黑色和白色的机器人都走到同一个格子,那么我优先把这个格子给黑色的机器人。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int const MAXN = 1e6 + 10;
int n, m, T, white[MAXN], black[MAXN], color[MAXN], f[MAXN][21];
string s;
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
while (T--) {
cin >> n >> m;
int size = n * m;
for (int i = 0; i < size; ++i) black[i] = white[i] = 0;
for (int i = 0; i < n; ++i) {
cin >> s;
for (int j = 0; j < m; ++j) color[i * m + j] = s[j] - '0';
}
for (int i = 0; i < n; ++i) {
cin >> s;
for (int j = 0; j < m; ++j) {
int pos = i * m + j;
if (s[j] == 'R')
f[pos][0] = pos + 1;
else if (s[j] == 'L')
f[pos][0] = pos - 1;
else if (s[j] == 'U')
f[pos][0] = pos - m;
else
f[pos][0] = pos + m;
}
}
for (int j = 1; j <= 20; ++j) // 遍历不同长度的区间
for (int i = 0; i < size; ++i) // 左边界遍历
f[i][j] = f[f[i][j - 1]][j - 1]; // 进行dp操作
for (int i = 0; i < size; ++i) {
int to = i;
for (int j = 20; j >= 0; --j)
if ((1 << j) & size) to = f[to][j];
color[i] ? white[to] = 1 : black[to] = 1;
}
int res_all = 0, res_black = 0;
for (int i = 0; i < size; ++i) {
if (black[i])
res_all++, res_black++;
else if (white[i])
res_all++;
}
cout << res_all << " " << res_black << endl;
}
return 0;
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)