RMQ

RMQ的定义:

  RMQ是询问某个区间内的最值,主要以ST表的方式实现。

   在一般的问题中,经常需要维护区间的最值,此时就可以使用RMQ来维护了。

ST表:

  1.预处理

  用动态规划的思想来实现,但不支持在线修改。

   用dp[i][j]表示以i为起点,长度为2^j的区间的最值,也就是区间[i,i+2^j-1]的最值。

   因为维护的是最值,所以一个区间的最值可以由两个小区间的最值组成(要求这两个小区间完全覆盖大区间);

   转移:dp[i][j]=max/min{dp[i][j-1],dp[i+(1<<(j-1))][j-1]};表示区间[i,i+(1<<j)-1]的最值为区间[i,i+(1<<(j-1))-1]和区间[i+(1<<(j-1)),i+(1<<j)]这两个区间的最值。

   初始化:dp[i][0]=val[i];

   时间复杂度:O(NlogN);

   图示:i+2^(j-1)-1属于前区间,i+2^(j-1)属于后区间。

    

  

  2.询问

  对于一个区间可以用两个区间来覆盖,所以只要知道这两个区间就可以通过预处理完了的dp数组求得最值。

  先找出区间[x,y]的长度所对应的最大的2的幂,也就是求出一个最大的K,满足2^k<=n。

  有了K之后可以将区间化为[x,x+(1<<k)],[y-(1<<k)+1,y]。

  图示:

     

例题引入:

  L2880 [USACO07JAN]平衡的阵容Balanced Lineup: https://www.luogu.org/problemnew/show/P2880

  每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John 决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比.

  但是为了避免水平悬殊,牛的身高不应该相差太大. John 准备了Q (1 <= Q <= 180,000) 个可能的牛的选择和所有牛的身高 (1 <= 身高 <= 1,000,000). 他想知道每一组里面最高和最低的牛的身高差别.  

例题解答:

  模板题,直接用RMQ来维护每组里的Max和Min就行了。

  

代码:

#include<bits/stdc++.h>
using namespace std;
#define register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 50009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
int dp[maxn][20],f[maxn][20];
int n,m,k,ans,tot;
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
//    memset(f,INF,sizeof(f));
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        dp[i][0]=x,f[i][0]=x;
    }
    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]);
            f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        int k=log(y-x+1)/log(2);
        printf("%d
",max(dp[x][k],dp[y-(1<<k)+1][k])-min(f[x][k],f[y-(1<<k)+1][k]));    
    }    
    fclose(stdin);
    fclose(stdout);
    return 0;
}

习题报告:

   L2471 [SCOI2007]降雨量 :https://www.luogu.org/problemnew/show/P2471

  解题思路:

  一道毒瘤题...毒得一批...

  首先这题可以想到用东西维护区间的最大值,然后就是一批毒瘤的判断了。

  由于年份递增,所以在询问的时候可以二分查找最后一个小于等于当前输入的X,Y年的下标,(请默认X<Y)

  设当前找到的X下标为nowx,Y为nowy,X值表示为Vx,Y值表示为Vy。

  1.X值和Y值都已知区间[nowx+1,nowy-1]的最大值为mx                  

    false:Vy>Vx||mx>=Vy

   maybe:Vy>mx&&区间[nowx,nowy]之间没有未知降雨量的年份&&mx<Vy

   maybe:Vy>mx&&区间[nowx,nowy]之间有未知降雨量的年份&&mx<Vy

  2.X值已知,Y值未知,区间[nowx+1,nowy-1]的最大值为mx

   false:mx>=Vx

   maybe:mx<Vx&&区间有未知年份

   3.X值未知,Y值已知,区间[nowx,nowy-1]的最大值为mx

   false:Vy<=mx

   maybe:Vy>mx&&区间有未知年份

  4.X值,Y值都未知

   只有maybe

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 50009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
int n,m,k,ans,tot,Q,cnt;
int dp[maxn<<1][25],year[maxn<<1];

int Query(int x,int y)
{
    if(y<x)
        return -INF;
    int k=log2((double)y-x+1);
    return max(dp[x][k],dp[y-(1<<k)+1][k]);
}
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
        year[i]=read(),dp[i][0]=read();
    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]);
    m=read();
    for(int i=1;i<=m;i++)
    {
        int s=read(),t=read();
        int f,nowx,nowy,mx;
        bool havx,havy;
        nowx=lower_bound(year+1,year+1+n,s)-year,//查找年份为s的下标 
        nowy=lower_bound(year+1,year+1+n,t)-year;//          y 
        havx=(s==year[nowx]&&nowx<=n),havy=(t==year[nowy]&&nowy<=n);
        //havx若为1则表明第x年的降水量已知,反之为未知
        //havy同havx 
        if(havx)//已知x值 
        {
            if(havy)//已知x值和y值 
            {
                mx=Query(nowx+1,nowy-1);//x值和y值已知,只需要查询 x+1->y-1的最值 
                if(dp[nowy][0]<=mx||dp[nowy][0]>dp[nowx][0])//x的值<y的值||y值<=mx为false 
                    f=0;
                else if(dp[nowy][0]>mx)// y值>mx,可能为true 
                {
                    if(nowy-nowx==t-s)//中间没有年份未知,为true 
                        f=1;
                    else//有年份未知,为maybe 
                        f=2;
                }
            }
            else//x值已知,y值未知 
            {
                mx=Query(nowx+1,nowy-1);//虽然只是已知x,y还未知,但查询x+1->y-1和x值比较 
                if(dp[nowx][0]>mx)//mx<x值,maybe 
                    f=2;
                else//mx>=x值为false 
                    f=0;
            }
        }
        else//未知x值 
        {
            if(havy)//未知x值,已知y值 
            {
                mx=Query(nowx,nowy-1);//查询x->y-1的最值与y值进行比较 
                if(dp[nowy][0]<=mx)//y值<=mx 为false 
                    f=0;
                else//为maybe 
                    f=2;
            }
            else//x值,y值都未知,一定为maybe 
                f=2;
        }
        if(f==2)
            puts("maybe");
        else if(f==0)
            puts("false");
        else
            puts("true");
     } 
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

   

  

原文地址:https://www.cnblogs.com/Dxy0310/p/9747890.html