[笔记] RMQ区间极值问题

专题:区间最小/最大查询(Range Minimum/Maximum Query)RMQ问题
[描述] 已知长度为L的数列A,询问区间[l, r]中的最值。
若询问的次数较少,可以用线性的复杂度来查询,但如果询问的次数过多且L过大,那么复杂度就会很高。所以需要更快速的查找方法。
ST算法:预处理O(nlogn),查询指定区间的最值O(1)。
把待求区间[l, r]分为两段长为len的区间,左边一段为[l, l+len-1],右边一段为[r-len+1, r],len必须使得两段区间覆盖待求区间。
设所求数组为W:
那么,所求最小值就是两个区间的最小值的最小值:
即Min(Min{W[i], l<=i<=l+len-1}, Min{W[j], r-len+1<=j<=r})
若都在预先处理中先求得两个区间的最小值,则每次查询的复杂度都是O(1)。
预处理:(DP)
   设dp[i][j]表示从第i个数起连续2^j个数中的最值。
   dp[i][0]=a[i]1<=i<=N
   dp[i][j]=Min(dp[i][j-1], dp[i+2^(j-i)][j-1])
求最值:把区间[l, r]分成两个长度为2^n的区间。
   为使区间被分解后,长度为2^n,区间部分可重叠,但不可越过[l, r]。
   例如 [3, 9] = [3, 6]+[6, 9]即:dp[3, 2]和dp[6, 2]

模板: (Toj_2762)
#define MAXN 50005
#define Lowbit(x) ((x)&(-x))
int dmax[20][MAXN], dmin[20][MAXN];
int a[MAXN], p[MAXN];
int n, m;

void init() {
    int i, j, k;
    for (i=1; i<=n; i++) {
        dmax[0][i]=a[i];
        dmin[0][i]=a[i];
    }
    p[0]=p[1]=0;
    for (i=2; i<=n; i++) p[i]=p[i-1]+(Lowbit(i)==i);
    for (i=1, k=1; k<=n; i++, k*=2)
        for (j=n; j>0; j--)
            if (j+(1<<(i-1))<=n) {
                dmax[i][j]=Max(dmax[i-1][j], dmax[i-1][j+(1<<(i-1))]);
                dmin[i][j]=Min(dmin[i-1][j], dmin[i-1][j+(1<<(i-1))]);
            }
}
int query(int l, int r) {
    int k=p[r-l+1];
    return Max(dmax[k][l], dmax[k][r-(1<<k)+1])-Min(dmin[k][l], dmin[k][r-(1<<k)+1]);
}
以前写的时候每次查询都是现求对数,直接导致查询复杂度上升到O(log),后来才被告知应该提前求出打个表,查询复杂度就是O(1)了。


初九


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值