【倍增】RMQ的ST表算法

RMQ问题:给定一个长度为N的区间,M个询问,每次询问Li到Ri这段区间元素的最大值/最小值。

RMQ的高级写法一般有两种,即为线段树和ST表。

本文主要讲解一下ST表的写法。(以区间最大值为例)

ST表:一种利用dp求解区间最值的倍增算法。

定义:f[i][j]表示i到i+2^j-1这段区间的最大值。

预处理:f[i][0]=a[i]。即i到i区间的最大值就是a[i]。

状态转移:将f[i][j]平均分成两段,一段为f[i][j-1],另一段为f[i+2^(j-1)][j-1]。

两段的长度均为2^j-1。f[i][j]的最大值即这两段的最大值中的最大值。

得到f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。

void RMQ(int N){
    /*注意外部循环从j开始,
    因为初始状态为f[i][0],
    以i为外层会有一些状态遍历不到。*/
    for(int j=1;j<=20;j++)
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

查询:需要查询的区间为[i,j],则需要找到两个覆盖这个闭区间的最小幂区间。

这两个区间可以重复,因为两个区间是否相交对区间最值没有影响。(如下图)

当然求区间和就肯定不行了。这也就是RMQ的限制性。

因为区间的长度为j-i+1,所以可以取k=log2(j-i+1)。

则RMQ(A,i,j)=max(f[i][k],f[j-2^k+1][k])。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int a[100001],f[100001][20];
inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c==‘-‘)
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-‘0‘;
    return x*f;
}
void RMQ(int N){
    for(int j=1;j<=20;j++)
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int main(){
    int N=read(),M=read();
    for(int i=1;i<=N;i++) a[i]=read();
    for(int i=1;i<=N;i++) f[i][0]=a[i];
    RMQ(N);
    while(M--){
        int i=read(),j=read();
         int k=(int)(log((double)(j-i+1))/log(2.0));
        printf("%d\n",max(f[i][k],f[j-(1<<k)+1][k]));
    }
    return 0;
}
时间: 2024-10-17 17:58:43

【倍增】RMQ的ST表算法的相关文章

倍增思想到ST表RMQ

Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 36864   Accepted: 17263 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer Joh

RMQ与st表

模板题 P3865 [模板]ST表 代码 实质也是DP,利用倍增获取从i开始长度为\(2^0,2^1,2^2-2^j\)的区间内的最大值. 这样对于任意区间\([l,r]\)都有,令\(dis=r-1+1\)则有\(k_0=2^c,2^{k_0}≤dis≤2^{k_0+1}\)这样在区间\([l,l+k0-1],[r-k0+1,r]\)完全覆盖了这个区域(中间可能有重叠) #include <iostream> #include <cmath> #include <cstdi

RMQ问题 - ST表的简单应用

2017-08-26 22:25:57 writer:pprp 题意很简单,给你一串数字,问你给定区间中最大值减去给定区间中的最小值是多少? 用ST表即可实现 一开始无脑套模板,找了最大值,找了最小值,分别用两个函数实现,实际上十分冗余 所以TLE了 之后改成一个函数中同时处理最大值和最小值,就可以了 AC代码如下: /* @theme:poj 3264 @writer:pprp @declare:ST表(sparse table)稀疏表,用动态规划的思想来解决RMQ问题: @date:2017

Codeforces 803G Periodic RMQ Problem ST表+动态开节点线段树

思路: (我也不知道这是不是正解) ST表预处理出来原数列的两点之间的min 再搞一个动态开节点线段树 节点记录ans 和标记 lazy=-1 当前节点的ans可用  lazy=0 没被覆盖过 else 区间覆盖 push_up的时候要注意好多细节,, 数组尽量往大开 //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const in

RMQ(st在线算法模板)

#include<iostream> #include<cmath> #include<algorithm> using namespace std; #define M 100010 #define MAXN 500 #define MAXM 500 int dp[M][18]; /* *一维RMQ ST算法 *构造RMQ数组 makermq(int n,int b[]) O(nlog(n))的算法复杂度 *dp[i][j] 表示从i到i+2^j -1中最小的一个值(

算法详解——st表

st表是解决区间RMQ(区间最值问题)的一类算法,时间复杂度为O(nlogn)的预处理和O(1)的查询,其主要运用了类似倍增的思想... 总体来说,st表的用处还是挺大的,代码也比较短,容易记... st表 若现在给定一个长度为n的序列A,每次给定两个数l,r,求出A[l]~A[r]中的最大值... 那么我们考虑怎么做,首先是朴素做法,每次询问最大值时用for循环从l到r跑一遍,每次循环复杂度为O(r - l) 那有没有一种算法可以让我们以O(1)的复杂度就知道最大值呢? 这时我们引进st表算法

hdu6107 倍增法st表

发现lca的倍增解法和st表差不多..原理都是一样的 /* 整篇文章分成两部分,中间没有图片的部分,中间有图片的部分 分别用ST表求f1,f2表示以第i个单词开始,连续1<<j行能写多少单词 */ #include<bits/stdc++.h> #define FIN freopen("in.txt","r",stdin); using namespace std; #define ll long long #define MX 100005

ST表超级详解

ST表超级详解 关于ST表,有很多文章,这里本蒟蒻也来发一波~~ 希望能为您提供帮助~~ 1.ST表的介绍 ST表算法全称Sparse-Table算法,是由Tarjan提出的一种解决RMQ问题(区间最值)的强力算法.离线预处理时间复杂度 θ(nlogn),在线查询时间 θ(1),可以说是一种非常高效的算法.不过ST表的应用场合也是有限的,它只能处理静态区间最值,不能维护动态的,也就是说不支持在预处理后对值进行修改.一旦修改,整张表便要重新计算,时间复杂度极高.动态最值可以用线段树.树状数组等来维

[模板]ST表浅析

ST表,稀疏表,用于求解经典的RMQ问题.即区间最值问题. Problem: 给定n个数和q个询问,对于给定的每个询问有l,r,求区间[l,r]的最大值.. Solution: 主要思想是倍增和区间dp. 状态:dp[i][j] 为闭区间[i,i+2^j-1]的最值. 这个状态与转移方程的关系很大,即闭区间的范围涉及到了转移方程的简便性. 转移方程:dp[i][j]=max(dp[i][j-1],dp[i+2^(j-1)][j-1]). 这是显然的,但这里有个细节:第一个项的范围为[i,i+2^