【引子】RMQ (Range Minimum/Maximum Query)问题:

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

{方法}

           1、朴素(即搜索),O(n)-O(qn) online。
           2、线段树,O(n)-O(qlogn) online。
    3、ST(实质是动态规划),O(nlogn)-O(q) online。
        ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是 max(d[a,k], d[b-2^k+1,k]),        其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
        d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
    4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(q) online。
        首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1        的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。

【例】给定数组,询问区间最小值。(无修改)
    (数据范围不用线段树)

【解】可以写一个线段树,但是预处理和查询的复杂度都是O(logn),存心的话可以给你卡掉。

  所以采用ST算法,它可以做到O(nlogn)的预处理,O(1)地回答每个询问

  f[i][j]表示数组p从位置i开始到位置i+2^j-1的最小值
  f[i][j]=min(f[i+(1<<(j-1))][j-1],f[i][j-1]);f[i][0]=p[i].
  求a~b的最小值,就是找出比b-a+1小的最大的二的幂次k
  有ans=min(f[a][k],f[b-(1<<k)+1][k])

【原理】

nlogn预处理出Min[][]和Max[][],查询的时候O(1)查询。

Max[j][i]或Min[j][i]代表,从j的位置开始,长度为2^i的子段中的最大值或最小值。

然后预处理的时候递推。

询问的时候先算出[l,r]的长度的2的对数,然后取出答案即可

是一种优秀的存取方法。

【实现】(以最大值为例):
    首先是预处理,用一个DP解决。设a[i]是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5
6 8 1 2 9 7
,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。
f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f[i,0]其实就等于a[i]。这样,Dp的状态、初值都
已经有了,剩下的就是状态转移方程。我们把f[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从i到i+2^(j-1)-1为一
段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5

6,8,1,2这两段。f[i,j]就是这两段的最大值中的最大值。于是我们得到了动规方程F[i,j]=max(F[i,j-1],F[i+2^(j-
i),j-1]).
     
  
 接下来是得出最值,也许你想不到计算出f[i,j]有什么用处,想计算max还是要O(logn),甚至O(n)。但有一个很好的办法,做到了
O(1)。还是分开来。如在上例中我们要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由
f[2,2]和f[5,2]得到。扩展到一般情况,就是把区间[l,r]分成两个长度为2^n的区间(保证有f[i,j]对应)

【模板代码】

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstdlib>
#include<iomanip>
#include<cassert>
#include<climits>
#define maxn 100001
#define F(i,j,k) for(int i=j;i<=k;i++)
#define M(a,b) memset(a,b,sizeof(a))
#define FF(i,j,k) for(int i=j;i>=k;i--)
#define inf 0x7fffffff
#define maxm 21
using namespace std;
int read(){
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int fm[maxn][maxm],fi[maxn][maxm],p[maxn];
int n,q;
inline int init()
{
cin>>n>>q;
F(i,,n){
cin>>p[i];
}
F(i,,n){
fm[i][]=fi[i][]=p[i];
}
int m=floor((int)(log10((double)n)/log10((double))));
F(j,,m)F(i,,n){
fm[i][j]=max(fm[i+(<<(j-))][j-],fm[i][j-]);
fi[i][j]=min(fi[i+(<<(j-))][j-],fi[i][j-]);
}
}
inline int stmax(int a,int b)
{
int m=floor((int)(log10((double)(b-a+))/log10((double))));
return max(fm[a][m],fm[b-(<<m)+][m]);
}
inline int stmin(int a,int b)
{
int m=floor((int)(log10((double)(b-a+))/log10((double))));
return min(fi[a][m],fi[b-(<<m)+][m]);
}
int main()
{
std::ios::sync_with_stdio(false);//cout<<setiosflags(ios::fixed)<<setprecision(1)<<y;
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
init();int c,d;
while(q--)
{
int a,b;
cin>>a>>b;
if(a>b) swap(a,b);
c=stmax(a,b);
d=stmin(a,b);
cout<<c<<endl<<d<<endl;
}
return ;
}

ST

最新文章

  1. Longest Increasing Path in a Matrix -- LeetCode 329
  2. MySQL缺失mysql_config文件
  3. 关于WebDAV带来的网站潜在安全问题的疑问
  4. Java防盗链机制
  5. LeetCode Number of Digit One
  6. Tomcat类加载器机制
  7. Python学习教程(learning Python)--3.1 Python的if分支语句
  8. Java从服务器上获取时间,动态在jsp页面显示
  9. Django如何设置proxy
  10. 使用boost中的线程池
  11. Just learn how to use the JNI
  12. Android开发之模板模式初探
  13. 绑定网关mac,防arp攻击
  14. Spring Security Filter详解
  15. 在Linux环境下实现一个非常好的bash脚本框架
  16. 【LeetCode每天一题】 Merge k Sorted Lists(合并K个有序链表)
  17. 【oracle】oracle常用命令汇总
  18. [WC2014]时空穿梭(莫比乌斯反演)
  19. 关于Safari浏览器使用的几点总结
  20. Unix/Linux文件类型及访问权限

热门文章

  1. 使用jdk自带的工具native2ascii 转换Unicode字符和汉字
  2. SGU 209. Areas
  3. 【LOJ】#2014. 「SCOI2016」萌萌哒
  4. 字符串处理strcpy strcat函数的用法
  5. LoadRunner的Capture Level说明
  6. HDU - 4777 离线树状数组
  7. String 不变性以及 Java 值传递和引用传递
  8. Java 单例模式的七种写法
  9. Java8所有的包介绍(由英文文档翻译而来)
  10. java 数组冒泡排序、转置(降序)