RMQ问题

RMQ问题

RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。

主要方法(线段树,ST表,LCA)

1.线段树

支持修改,时间复杂度:预处理O(n),查询O(log(n))

例:HDU1754

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
//定义 
#define maxn 500007  //元素总个数  
int Max[maxn<<2],Add[maxn<<2];//Max求最大值,Add为懒惰标记   
int A[maxn],n,ANS;//存原数组数据下标[1,n] 
//建树
//PushUp函数更新节点信息 ,这里是求最大值 
void PushUp(int rt){Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);}  
//Build函数建树   
void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号  
    if(l==r) {//若到达叶节点   
        Max[rt]=A[l];//储存数组值   
        return;  
    }  
    int m=(l+r)>>1;  
    //左右递归   
    Build(l,m,rt<<1);  
    Build(m+1,r,rt<<1|1);  
    //更新信息   
    PushUp(rt);  
}   
//点修改
void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号  
    if(l==r){//到叶节点,修改   
        Max[rt]=C;  
        return;  
    }  
    int m=(l+r)>>1;  
    //根据条件判断往左子树调用还是往右   
    if(L <= m) Update(L,C,l,m,rt<<1);  
    else       Update(L,C,m+1,r,rt<<1|1);  
    PushUp(rt);//子节点更新了,所以本节点也需要更新信息   
}   

//区间查询
int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号  
    if(L <= l && r <= R){  
        //在区间内,直接返回   
        return ANS=max(Max[rt],ANS);  
    }  
    int m=(l+r)>>1;   
      
    //累计答案    
    if(L <= m) Query(L,R,l,m,rt<<1);  
    if(R >  m) Query(L,R,m+1,r,rt<<1|1);  
    return ANS;  
}   
int main()
{
		int m,u,v;
	while(cin>>n>>m)
	{
		memset(Max,0,sizeof(Max));
		memset(Add,0,sizeof(Add));
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&A[i]);
	} 
	Build(1,n,1);
	string str;
	for(int i=1;i<=m;i++)
	{
		cin>>str;
		if(str[0]=='Q')
		{
			ANS=0;
			scanf("%d%d",&u,&v);
			printf("%d\n",Query(u,v,1,n,1));
		}
		else if(str[0]=='U')
		{
			scanf("%d%d",&u,&v);
			Update(u,v,1,n,1);
		}
	}
	}
}

2.ST表

以倍增思想为基础,不能在线修改,适合离线RMQ问题
时间复杂度: 预处理O(nlog(n)) 查询O(1)
预处理:先开辟一个数组\(f[i][j]\)表示从第i号节点到第i+2^j-1号节点的最大值,即从i号节点开始往后数共2^j个节点中的最大值。
查询:查询x,y之间的最大值,记len为[x,y]区间长度 ,最大值=max\((f[x][log {len}],f[y-2^{log{len}}+1][log{len}])\)
具体思路参考这篇博客

例:洛谷p3865

#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimize ("unroll-loops")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define lowbit(a) ((a)&-(a))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int a[110000][30],logg[110000],bin[110000];
int i,j,k,m,n,o,p,js,jl,x,y,t,lg;
int my_max(int x,int y)
{
    if(x>y)return(x);
    else return(y);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i][0]);

    bin[0]=1;for(int i=1;i<=30;i++)bin[i]=bin[i-1]*2;
    logg[0]=-1;for(int i=1;i<=n;i++)logg[i]=logg[i/2]+1;

    for(int i=1;i<=logg[n];i++)
    for(int j=1;j<=n;j++)
    {
        if(j+bin[i]-1<=n)
        {
            a[j][i]=my_max(a[j][i-1],a[j+bin[i-1]][i-1]);
        }
    } 

    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        t=y-x+1;lg=logg[t];
        printf("%d\n",my_max(a[x][lg],a[y-bin[lg]+1][lg]));
    }

    return 0;
}

3.LCA

太菜了,还不会....以后再写

你可能感兴趣的:(RMQ问题)