• RMQ ---(ST算法)


    RMQ ---(ST算法)

    RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i,j之间的最小/大值。如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来。在这里介绍一个在线算法。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

     

    1.首先做预处理(以处理区间最小值为例)

    设st[i][j]表示从第i位开始连续2^j个数中的最小值。例如st[i][1]表示的是i位置和i+1位置中两个数的最小值

    之后我们很容想到递推方程:

    st[i][j] = min(st[i][j - 1], st[i + (1 << j - 1)][j - 1])

    1 for(int j=1; (1<<j)<=n; j++){
    2         for(int i=1; i+(1<<j)<=n+1; i++){
    3             mn[i][j] = min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
    4         }
    5     }

    2.查询

    假设我们需要查询区间[l, r]中的最小值,令k = log2(r - l + 1); 则区间[l, r]的最小值RMQ[l,r] = min(st[l][k], st[r - (1 << k) + 1][k]);

    那么为什么这样就可以保证为区间最值吗?

    st[l][k]维护的是[l, l + 2 ^ k - 1], st[r - (1 << k) + 1][k]维护的是[r - 2 ^ k + 1, r] 。

    那么我们只要保证r - 2 ^ k + 1 <= l + 2 ^ k - 1就能保证RMQ[l,r] = min(st[l][k], st[r - (1 << k) + 1][k]);

    我们用分析法来证明下:

    若r - 2 ^ k + 1 <= l + 2 ^ k - 1;

    则r - l + 2 <= 2 ^ (k + 1);

    又因为 k = log2(r - l + 1);

    则r - l + 2 <= 2 *(r - l + 1);

    则r - l >= 0;

    显然可得。

    由此得证。

    我们来举个例子 l = 4, r = 6;

    此时k = log2(r - l + 1) = log2(3) = 1;//向下取整

    所以RMQ[4, 6] = min(st[4][1], st[5][1]);

    st[4][1] = 4, st[5][1] = 2;

    所以RMQ[4, 6] = min(mn[4][1], mn[5][1]) = 2;

    我们很容易看出来了答案是正确的。

    模板代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn = 1e5+5;
     5 
     6 int a[maxn];
     7 int st[maxn][25];
     8 int n,q,l,r;
     9 int Log2[maxn];
    10 
    11 void init(){
    12     for(int i=0;i<=n;i++){
    13         if( i==0 ){
    14             Log2[i] = -1;
    15         }
    16         else{
    17             Log2[i] = Log2[i>>1]+1;
    18         }
    19     }
    20     for(int j=1; (1<<j)<=n; j++){
    21         for(int i=1; i+(1<<j)<=n+1; i++){
    22             st[i][j] = min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    23         }
    24     }
    25 }
    26 
    27 int query(int l, int r){
    28     int k = Log2[r-l+1];
    29     return min(st[l][k],st[r-(1<<k)+1][k]);
    30 }
    31 
    32 void solve(){
    33     init();
    34     scanf("%d",&q);
    35     while( q-- ){
    36         scanf("%d%d",&l,&r);
    37         printf("%d
    ",query(l,r));
    38     }
    39 }
    40 
    41 int main()
    42 {
    43     while( ~scanf("%d",&n) ){
    44         for(int i=1;i<=n;i++){
    45             scanf("%d",a+i);
    46             st[i][0] = a[i];
    47         }
    48         solve();
    49     }
    50     return 0;
    51 }
  • 相关阅读:
    angular 中如何使用自定义组件
    angular组件数据和事件
    angular组件数据
    angular绑定数据
    angular自定义组件
    angular项目目录结构分析
    Angular 开发工具介绍
    从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构
    EF How to use context.Set and context.Entry, which ships with EF4.1 ?
    C# 向IQueryable添加一个Include扩展方法
  • 原文地址:https://www.cnblogs.com/wsy107316/p/12200058.html
Copyright © 2020-2023  润新知