区间最值问题-RQM(ST表,线段树)

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;
}

用单调队列更简单。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值