ST表,和二维ST表

2 篇文章 0 订阅
1 篇文章 0 订阅

RMQ
区间最值问题
问区间最值问题, 没有修改操作

ST表:

ST表:支持查询任意区间[l,r]的最值,但是不支持修改
优势:

  • 查询复杂度 O ( 1 ) O(1) O(1)
  • 建表复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
  • 代码量小

ST表的形式:预处理

ST表的本质:动态规划求区间最值

  1. 状态 d p i , j dp_{i,j} dpi,j i i i为起点长度为 2 j 2^j 2j的区间最值 min ⁡ / max ⁡ ( i , i + 2 j − 1 ) \min/\max(i,i+2^j-1) min/max(i,i+2j1)
  2. 方程 d p i , j = m i n / m a x ( d p i , j − 1 , d p i + 2 j − 1 , j − 1 ) dp_{i,j} = min/max(dp_{i,j-1}, dp_{i+2^{j - 1},j-1}) dpi,j=min/max(dpi,j1,dpi+2j1,j1)
  3. 初始化 d p i , 0 = a i dp_{i,0} = a_i dpi,0=ai
  4. 对于任意 区间如何查询
    1. l e n len len转换成二进制,把 [ l , r ] [l,r] [l,r]这个区间按照二进制长度完整的拆开 时间复杂度 O ( l o g n ) O(logn) O(logn)
      就不是 O ( 1 ) O(1) O(1)的了,与线段树和树状数组的优势少了一项
    2. [ l , r ] [l,r] [l,r]长度为 l e n = r − l + 1 len=r-l+1 len=rl+1先求出最大的 2 k ≤ l e n 2^k \leq len 2klen
      注意 最大值或最小值 重复计算是没有关系的 得式子: m i n / m a x ( d p l , k , d p r − 2 k + 1 , k ) min/max(dp_{l,k},dp_{r-2^k+1,k}) min/max(dpl,k,dpr2k+1,k)
void init()
{
    for (int j = 1; (1 << j) <= n; j++)
    {
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
        {
            dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            // dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r)
{
    int i = (int)(log2(r - l + 1));
    return min(dp[l][i], dp[r - (1 << i) + 1][i]);
    // return max(dp[l][i], dp[r - (1 << i) + 1][i]);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        dp[i][0] = a[i];
    }
    init();
    while (m--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", query(l, r));
    }
    return 0;
}

luogu习题

P3865

思路

ST表模板题

代码

#include <bits/stdc++.h>  
  
#define ll long long  
using namespace std;  
const int inf = 0x3f3f3f3f;  
const int MOD = 1e9 + 7, N = 1e5 + 5;  
int dp[N][25];  
int n, m;  
  
void initST()  
{  
    for (int j = 1; (1 << j) <= n; ++j)  
        for (int i = 1; i + (1 << j) - 1 <= n; i++)  
            dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);  
}  
  
int query(int l, int r)  
{  
    int k = (int)(log2(r - l + 1));  
    return max(dp[l][k], dp[r - (1 << k) + 1][k]);  
}  
  
int main()  
{  
    scanf("%d%d", &n, &m);  
    for (int i = 1; i <= n; i++)  
        scanf("%d", &dp[i][0]);  
    initST();  
    while (m--)  
    {  
        int l, r;  
        scanf("%d%d", &l, &r);  
        printf("%d\n", query(l, r));  
    }  
    return 0;  
}

二维ST表

这里讨论的是正方形,而不是矩形,矩形在正方形的基础上加一维即可

  1. 状态 s t [ i ] [ j ] [ k ] st[i][j][k] st[i][j][k]表示从 ( i , j ) (i, j) (i,j)为起点的长度和高度为 2 k 2^k 2k的正方形的最大值
  2. 转移
    x x x 2 k − 1 2^{k-1} 2k1
    s t [ i ] [ j ] [ k ] = max ⁡ ( s t [ i ] [ j ] [ k − 1 ] , s t [ i + x ] [ j ] [ k − 1 ] , s t [ i ] [ j + x ] [ k − 1 ] , s t [ i + x ] [ j + x ] [ k − 1 ] ) st[i][j][k] = \max(st[i][j][k-1],st[i+x][j][k-1],st[i][j+x][k-1],st[i+x][j+x][k-1]) st[i][j][k]=max(st[i][j][k1],st[i+x][j][k1],st[i][j+x][k1],st[i+x][j+x][k1])
  3. 初始化 d p [ i ] [ j ] [ 0 ] = a [ i ] [ j ] dp[i][j][0]=a[i][j] dp[i][j][0]=a[i][j]
  4. 对于任意矩形如何查询
    k k k log ⁡ 2 ( s z ) \log_2(sz) log2(sz)(长度 / / / 宽度) x x x 2 k 2^k 2k
    max ⁡ ( s t [ s x ] [ s y ] [ k ] , s t [ s x ] [ f y − x + 1 ] [ k ] , s t [ f x − x + 1 ] [ s y ] [ k ] , s t [ f x − x + 1 ] [ f y − x + 1 ] [ k ] ) ; \max({st[sx][sy][k], st[sx][fy-x+1][k], st[fx-x+1][sy][k], st[fx-x+1][fy-x+1][k]}); max(st[sx][sy][k],st[sx][fyx+1][k],st[fxx+1][sy][k],st[fxx+1][fyx+1][k]);
    模板:
#include <bits/stdc++.h>  
#define ll long long  
#define PII pair<int, int>  
using namespace std;  
const int inf = 0x3f3f3f3f;  
const int MOD = 1e9 + 7, N = 1005;  
int A, B, n;  
int lg[N];  
int a[N][N];  
int st[N][N][11];  
  
void init()  
{  
    for (int k = 1; k <= 10; ++k)  
    {  
        for (int i = 1; i + (1 << k) - 1 <= A; ++i)  
        {  
            for (int j = 1; j + (1 << k) - 1 <= B; ++j)  
            {  
                int x = 1 << (k - 1);  
                st[i][j][k] = max({st[i][j][k - 1], st[i + x][j][k - 1],  
                                      st[i][j + x][k - 1], st[i + x][j + x][k - 1]});  
            }  
        }  
    }  
}  
  
// 第一个数是最大值,第二个数是最小值 int query(int sx, int sy, int fx, int fy)  
{  
    int k = lg[fx - sx + 1], x = (1 << k) - 1;  
  
    int mx = max({st[sx][sy][k], st[sx][fy - x][k], st[fx - x][sy][k], st[fx - x][fy - x][k]});  
    return mx;  
}  
  
int main()  
{  
    scanf("%d%d%d", &A, &B, &n);  
    for (int i = 2; i <= 1000; ++i) lg[i] = lg[i / 2] + 1;  
    for (int i = 1; i <= A; ++i)  
        for (int j = 1; j <= B; ++j)  
        {  
            scanf("%d", &a[i][j]);  
            st[i][j][0] = a[i][j];  
        }  
    init();  
    // ......  
    return 0;  
}

例题:P2216 [HAOI2007] 理想的正方形

代码:
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int, int>
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7, N = 1005;
int A, B, n;
int lg[N];
int a[N][N];
int stMax[N][N][11], stMin[N][N][11];

void init()
{
	for (int k = 1; k <= 10; ++k)
	{
		for (int i = 1; i + (1 << k) - 1 <= A; ++i)
		{
			for (int j = 1; j + (1 << k) - 1 <= B; ++j)
			{
				int x = 1 << (k - 1);
				stMax[i][j][k] = max({stMax[i][j][k - 1], stMax[i + x][j][k - 1],
									  stMax[i][j + x][k - 1], stMax[i + x][j + x][k - 1]});
				stMin[i][j][k] = min({stMin[i][j][k - 1], stMin[i + x][j][k - 1],
									  stMin[i][j + x][k - 1], stMin[i + x][j + x][k - 1]});
			}
		}
	}
}

// 第一个数是最大值,第二个数是最小值 
PII query(int sx, int sy, int fx, int fy)
{
	int k = lg[fx - sx + 1], x = (1 << k) - 1;
	
	int mx = max({stMax[sx][sy][k], stMax[sx][fy - x][k], stMax[fx - x][sy][k], stMax[fx - x][fy - x][k]});
	int mn = min({stMin[sx][sy][k], stMin[sx][fy - x][k], stMin[fx - x][sy][k], stMin[fx - x][fy - x][k]});
	return {mx, mn};
}

int main()
{ 
	scanf("%d%d%d", &A, &B, &n);
	for (int i = 2; i <= 1000; ++i) lg[i] = lg[i / 2] + 1;
	for (int i = 1; i <= A; ++i)
		for (int j = 1; j <= B; ++j)
		{
			scanf("%d", &a[i][j]);
			stMax[i][j][0] = stMin[i][j][0] = a[i][j];
		}
	init();
	int ans = inf;
	for (int i = 1; i <= A - n + 1; ++i)
	{
		for (int j = 1; j <= B - n + 1; ++j)
		{
			PII x = query(i, j, i + n - 1, j + n - 1);
			ans = min(ans, x.first - x.second);
		}
	}
	printf("%d", ans);
	return 0;
}
  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值