1.ST表求解
ST表的实质其实是动态规划,下面是区间最小的递归公式,最大只需将min改成max即可
f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
二维数组的f[i][j]表示从i开始连续2*j个数的最小/大值。
例如:我们给出一个数组a[10]={0,1,2,4,6,7,9,2,12,3};
我们可以给f[i][0]....f[n][0]先初始化为上面数组的值,我们将f[i][j]分为两端,一段是i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1,f[i][j]就是分出来的这两个段的最大/小值。
于是有了递归公式。
写一道例题
P1816 忠诚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<set>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define TEST1 int T;cin>>T;while(T--)
#define TEST2 int T;T=1;while(T--)
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int N = 100010;
const int M = 20;
int base1 = 131, base2 = 13311;
int p1 = 1e9 + 7, p2 = 1e9 + 9;
const ll mod = 1e9 + 7;
int n, a[N], f[N][M],m[N][M];
//创建ST表
void create() {
//初始状态
//f[i][0]表示从i开始长度为2^0的区间最值为a[i]本身
for(int i = 1; i <= n; i ++) f[i][0] = a[i],m[i][0]=a[i];
int k = log2(n);
//枚举区间长度指数j
for(int j = 1; j <= k; j ++)
for(int i = 1; i + (1 << j) - 1 <= n; i ++)
{
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
m[i][j] = min(m[i][j - 1], m[i + (1 << j - 1)][j - 1]);
}
}
//利用ST表查询区间[L,R]的最大值
int query1(int L, int R) {
int k = log2(R - L + 1);
return max(f[L][k], f[R - (1 << k) + 1][k]);
}
int query2(int L,int R)
{
int k=log2(R-L+1);
return min(m[L][k], m[R - (1 << k) + 1][k]);
}
int main()
{
int m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
create();
while(m --) {
int L, R;
scanf("%d%d", &L, &R);
printf("%d ", query2(L,R));
}
}
其实就是区间最小值。
2.线段树
线段树其实也就是相当于区间分段,(a,b)的左孩子区间就是(a,(a+b)/2)和右孩子((a+b)/2+1,b),(a.b)区间的最值就是他分出的两个区间的最值的最值。
对于上道例题的代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<set>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define TEST1 int T;cin>>T;while(T--)
#define TEST2 int T;T=1;while(T--)
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int N = 2000010;
const int M = 1e9+2;
int base1 = 131, base2 = 13311;
int p1 = 1e9 + 7, p2 = 1e9 + 9;
const ll mod = 1e9 + 7;
ll n, m, a[N];
struct Node
{
int l, r, minn = mod;
}tree[N];
void build(int i, int l, int r)
{
tree[i] = { l,r };
if (l == r)
{
tree[i].minn = a[l];
return;
}
int mid = l + r >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
tree[i].minn = min(tree[i << 1].minn, tree[i << 1 | 1].minn);
}
int ask(int i, int l, int r)
{
if (l == tree[i].l && r == tree[i].r) return tree[i].minn;
int mid = tree[i].l + tree[i].r >> 1;
if (r <= mid) return ask(i << 1, l, r);
if (l > mid) return ask(i << 1 | 1, l, r);
return min(ask(i << 1, l, mid), ask(i << 1 | 1, mid + 1, r));
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
cout << ask(1, l, r) << " ";
}
}
int main()
{
TEST2
solve();
return 0;
}
3.其他例题
1.奶牛排队
1274. 奶牛排队 - AcWing题库
区间最大值与最小值的差
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<set>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define TEST1 int T;cin>>T;while(T--)
#define TEST2 int T;T=1;while(T--)
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int N = 100010;
const int M = 20;
int base1 = 131, base2 = 13311;
int p1 = 1e9 + 7, p2 = 1e9 + 9;
const ll mod = 1e9 + 7;
int n, a[N], f[N][M],m[N][M];
//创建ST表
void create() {
//初始状态
//f[i][0]表示从i开始长度为2^0的区间最值为a[i]本身
for(int i = 1; i <= n; i ++) f[i][0] = a[i],m[i][0]=a[i];
int k = log2(n);
//枚举区间长度指数j
for(int j = 1; j <= k; j ++)
for(int i = 1; i + (1 << j) - 1 <= n; i ++)
{
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
m[i][j] = min(m[i][j - 1], m[i + (1 << j - 1)][j - 1]);
}
}
//利用ST表查询区间[L,R]的最大值
int query1(int L, int R) {
int k = log2(R - L + 1);
return max(f[L][k], f[R - (1 << k) + 1][k]);
}
int query2(int L,int R)
{
int k=log2(R-L+1);
return min(m[L][k], m[R - (1 << k) + 1][k]);
}
int main()
{
int m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
create();
while(m --) {
int L, R;
scanf("%d%d", &L, &R);
printf("%d\n", query1(L,R)-query2(L,R));
}
}
2.求m区间内的最小值
P1440 求m区间内的最小值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<set>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define TEST1 int T;cin>>T;while(T--)
#define TEST2 int T;T=1;while(T--)
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int N = 2000010;
const int M = 1e9 + 2;
int base1 = 131, base2 = 13311;
int p1 = 1e9 + 7, p2 = 1e9 + 9;
const ll mod = 1e9 + 7;
ll n, m, a[N];
struct Node
{
ll l, r, minn = mod;
}tree[N];
void build(int i, int l, int r)
{
tree[i] = { l,r };
if (l == r)
{
tree[i].minn = a[l];
return;
}
int mid = l + r >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
tree[i].minn = min(tree[i << 1].minn, tree[i << 1 | 1].minn);
}
int ask(int i, int l, int r)
{
if (l == tree[i].l && r == tree[i].r) return tree[i].minn;
int mid = tree[i].l + tree[i].r >> 1;
if (r <= mid) return ask(i << 1, l, r);
if (l > mid) return ask(i << 1 | 1, l, r);
return min(ask(i << 1, l, mid), ask(i << 1 | 1, mid + 1, r));
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
for (int i = 1; i <= n; i++)
{
if (i == 1)
{
cout << "0\n";
continue;
}
int l=max(1ll,i-m), r=i-1;
cout << ask(1, l, r) << "\n";
}
}
int main()
{
TEST2
solve();
return 0;
}
用单调队列更简单。。。