zoukankan      html  css  js  c++  java
  • 【数据结构】ST表和RMQ算法

    ST表和RMQ算法

    给定一段区间([L,R]),要求我们给出([L,R])中某个子区间([L_{0},R_{0}])的最大值,我们可以临时从头到尾一个个比对这个子区间的元素来从这个子区间中找到最大值,但当要求我们去查询q次区间最值时,当q达到一定的次数时,根据容斥定理,一定会存在重复的查询,而也就意味着劳动的低效,于是我们不妨预先储存好一些区间和,通过区间的拼凑间接或者存好的区间的本身的完全匹配直接给出所询问的区间的答案。而当区间([L,R])的区间长度很大的时候,我们能不能尽可能少与准备区间同时又能用这些准备好的区间来恰好地能够表示出([L,R])中所有子区间的最值?

    • 设[L,R]的区间长度为n,则子区间长度为1的数量有n个,子区间长度为2的数量有n-2个,子区间长度为3的数量有n-3个,……,子区间长度为n的数量有1个。总子区间个数为(frac{(n+1) imes n}{2})个,单单去统计出所有的子区间将是一个(O(N^{2}))的工作量

    • 而我们可以将子区间的长度设置成是2的倍数,不妨求一下一共需要多少种以2的倍数为区间长度的区间的种类,于是有(2^{k}=n),解得最多有(log_{2}n)种类型,而每一种类型的区间的区间的数量为(n-2^{k})个,可以看成是一个关于n的常数,因而,我们大概只需要解决好(O(NlogN))的工作量。

    • 查询原理:要查询给定的([L_{0},R_{0}])​的最值,我们可以([L_{0},L_{0}+2^{i}])([R_{0}-2^{i}+1,R_{0}])​中各找到一个最大值,并从这两个最大值中再一决高下,最终就得到了([L_{0},R_{0}])​ 的最值。​

    • 就比如尼古拉丁赵四,郝建先生,乔纳森组成了A队,齐柏林,乔斯达,黑土组成了B队,尼古拉丁赵四,郝建先生,乔纳森,齐柏林,乔斯达,黑土组成了C队,请问C队中最高的人是谁?

      由于C队=A队+B队,我们不妨从A队中挑选出一个最高的,再从B队中挑选一个最高的,再从这两个人中确定一个最高的,而确定出的最高的就是C队的最高的。

    • 而查询原理的(L_0+2^{k})是大于(R_{0}-2^{k}+1),这样才能覆盖([L_{0},R_{0}])的所有的元素,且k的取值不会导致越界,即(R_{0}-2^{k}+1>L_{0})

      经过整理可得(2^{k}<R_{0}-L_{0}+1<2^{k+1})​​,解得k=trunc(log2( r-l+1 ));

    • 而了解了这些,我们应该怎么来实现预处理(将我们所需要的区间给算好)呢?

      动态规划?递推!?

      因为我们的设计好的区间长度都是2的倍数,那么我们可以将我们要计算的空间掰成对等的两半。(当然,长度为1的时候,就没有必要掰了,把它作为起始要初始化的条件)(就好像将巧克力棒掰成相同的两半一样)

      于是有f[a][b]=max(f[a][b-1],f[a+2<<(b-1)][b-1]);

      其中将f[a][b]定义为是以a为起点,b所相关得(2^{b})为区间长度

      for(int j=1;(1<<j)<=n;j++)///枚举区间长度
      {
          for(int i=1;i+(1<<j)-1<=n;i++)///枚举起点
          f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
      }
      

      哦对了,别忘了长度为1的初始化,直接让f[][0]等于对应点上的数值就好。

      for(int i=0;i<n;i++)
          f[i][0]=num[i];
      

      有了f[a][b]的定义后,对于区间[l,r]的最值的查询,我们就有了max([l,r])=max{f[l][k],f[r-(1<<k)+1][k]};

    ST表

    ST表(英文学名 sparse-table,俗称稀疏表)

    排版错了,太懒了不改了,经上文的介绍,ST表就是通过(O(NlogN))的用于处理来实现(O(1))的查询操作。

    RMQ算法

    RMQ(英文学名 range—maximum/maximum—query,区间最值查询)

    RMQ算法就是?借助ST表来实现的算法。

    模板题AcWing1273. 天才的记忆

    #include <bits/stdc++.h>
    #define MEM(a,x) memset(a,x,sizeof(a))
    #define W(a) while(a)
    #define gcd(a,b) __gcd(a,b)
    #define pi acos(-1.0)
    #define PII pair<int,int>
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define ll long long
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define MAX 1000005
    #define MOD 1000000007
    #define INF 0x3f3f3f3f
    #define lowbit(x) (x&-x)
    using namespace std;
    const int N = 2e5+10,M=18;
    int n,m;
    
    ll num[N],f[N][18];
    void ST(int n)
    {
    	repd(i,1,n)
    	    f[i][0] = num[i];
    	for(int j=1;(1<<j)<=n;j++)
    	    for(int i=1;i+(1<<j)-1<=n;i++)//注意要减一 
    	        f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    }
    ll RMQ(int l,int r)
    {
    	int k=log(r-l+1)/log(2);//换底公式 
    	return max(f[l][k],f[r-(1<<k)+1][k]);
    }
    int main()
    {
    	cin>>n;
    	repd(i,1,n)
    	    cin>>num[i];
    	ST(n);
    	cin>>m;
    	repd(i,1,m)
    	{
    		int l,r;
    		cin>>l>>r;
    		cout<<RMQ(l,r)<<endl;
    	}
        return 0;
    }
    
    
  • 相关阅读:
    数据结构-栈
    virtualenvwrapper 的安装和使用
    MySQL命令行本地登陆,远程登陆MySQL 的快捷键
    关于mysql8授权的问题,mysql萌新小白采坑记录
    史上最直接小白式的Sourcetree的分支创建与合并
    验证两个集合是否相等的方法
    VS开发工具的常用插件
    C# 数据库并发的解决方案(通用版、EF版)
    Jquery实现div左右重复来回走动
    描述符的应用
  • 原文地址:https://www.cnblogs.com/BeautifulWater/p/15120804.html
Copyright © 2011-2022 走看看