注:为方便描述算法 便于记忆 所以ST的代码用Pascal书写 见谅

RMQ,即Range Minimum/Maximum Query问题,给定一个区间,询问不同子区间的最值问题。

当询问次数较少时,朴素算法的时间尚可(暴力做法),k次询问,最坏情况是每次询问最大区间,时间复杂度O(kL),其中k表示询问次数,L表示给定的区间长度。

随着询问次数的增加,朴素算法(应该可以认为是n² 级别的算法)就显得太慢了,因此可以很方便想出线段树做法,节点存储区间最值。那么此时k次查询,L的区间长度,可知时间复杂度为O(k logL);L为定值,则log L为常数,随着k的增大线性增大,相比较朴素算法是大大优化了。

但是还不够。当 n过于大时,即使是线段树也不行。此时考虑ST(Sparse Table)算法。该算法的时间复杂度为两部分:预处理O(L logL),查询则为O(1)。

(关于构造笛卡尔树的方法日后另写qwq)


ST方法的原理类似DP。比如说求[1,100]的最值,如果我能知道[1,64]的最值以及[37,100]的最值,那么总区间的最值,一定是两个分区间中的一个最值。

这个例子很方便理解ST。对于分区间继续拆分,直到出现长度为2的区间;此时长度为2的区间最值就是原序列中两个数的较大值。

运用倍增就可以完成拆分。

于是开始搭建:

 begin
     init;
     readln(m);
      to m do
         begin
             readln(head,tail);
             if head>tail then
                 begin
                     temp:=head;
                     head:=tail;
                     tail:=temp;
                 end;
             writeln(query(head,tail));
         end;
 end.

init即预处理过程,m为询问的组数。不断读入[head,tail]的待查询区间,给出查询值。


预处理就可以写出来:

 procedure init;
     var
         i:longint;
     begin
         read(n);
          to n do
             read(a[i]);
         tableLength:=trunc(ln(n)/ln());
         createTable(tableLength);
         make;
     end;

行5-行7就是总区间的读入。之后要打一个2的幂表用以拆分(运用倍增),打出了这个表才可以分区间——例子中的64就是这么算出来的。显然这个表长(也就是最大次数)=logL,写在代码中用换底公式后向下取整,得到表长。

之后以表长建立2的幂表,完成后进入预处理的核心部分make。

建表的code比较简单,不多加赘述。

 procedure createTable(k:longint);
     var
         i:longint;
     begin
         powerTable[]:=;
         powerTable[]:=;
          to k do
             powerTable[i]:=*powerTable[i-];
     end;

预处理核心代码如下:

 procedure make;
     var
         i,j:longint;
     begin
          to n do
             line[i,]:=a[i];
          to tableLength do
               do
                 line[j,i]:=max(line[j,i-],line[j+powerTable[i-],i-]);
     end;

思想是DP的思想。对于总区间,先两端两端的划分,求其最值;再四段四段分,再八段八段分……line[i,j]表示序列中以i为起点,2的j次为长度的区间的最值。当j==0时自然就退化为当前位置的值。

要注意的是外层循环一定要是次数而不是起点,关于这点可以手动模拟一下感受感受;当外层循环为起点时,line数组求值中的依赖的有半段还未求出,故出错。


查询就比较简单了,由于预处理已经算出了所以可能用到的区间最值,只要做一次拆分,取两分区的max即可。

 function query(left,right:longint):longint;
     var
         t:longint;
     begin
         t:=trunc(ln(right-left+)/ln());
         exit(max(line[left,t],line[right-powerTable[t]+,t]));
     end;

完整代码如下(其实只是多了点定义和一个max函数):

 var
     a:..]of longint;
     i,n,m,head,tail,tableLength,temp:longint;
     line:..,..]of longint;
     powerTable:..]of longint;

 function max(a,b:longint):longint;
     begin
         if a>b then
             exit(a)
         else
             exit(b);
     end;

 procedure createTable(k:longint);
     var
         i:longint;
     begin
         powerTable[]:=;
         powerTable[]:=;
          to k do
             powerTable[i]:=*powerTable[i-];
     end;

 procedure make;
     var
         i,j:longint;
     begin
          to n do
             line[i,]:=a[i];
          to tableLength do
               do
                 line[j,i]:=max(line[j,i-],line[j+powerTable[i-],i-]);
     end;

 procedure init;
     var
         i:longint;
     begin
         read(n);
          to n do
             read(a[i]);
         tableLength:=trunc(ln(n)/ln());
         createTable(tableLength);
         make;
     end;

 function query(left,right:longint):longint;
     var
         t:longint;
     begin
         t:=trunc(ln(right-left+)/ln());
         exit(max(line[left,t],line[right-powerTable[t]+,t]));
     end;

 begin
     init;
     readln(m);
      to m do
         begin
             readln(head,tail);
             if head>tail then
                 begin
                     temp:=head;
                     head:=tail;
                     tail:=temp;
                 end;
             writeln(query(head,tail));
         end;
 end.

行32的循环上界要控制好,否则会段出错,切记切记。




但是仅仅是ST是不够的。如 P1440 求m区间内的最小值

此题数据规模在m≤n≤2000000内,虽然按理来说不可能但是ST方法会超时。即是加了快读快输也不行。。。

ST超时代码示例:

 #include <iostream>
 #include <algorithm>
 #include <cmath>

 using namespace std;

 ],line[][],a[],n,m;

 void createTable(int size){
     powerTable[] = ;
     ; i <= size; ++i){
         powerTable[i] =  * powerTable[i-];
     }
 }

 void init(){
     ; i <= n; ++i){
         line[i][] = a[i];
     }
     ; i <= floor(log(n)/log()); ++i){
         ; j <= n-powerTable[i]+; ++j){
             line[j][i] = min(line[j][i-],line[j+powerTable[i-]][i-]);
         }
     }
 }

 int query(int head,int tail){
     )/log());
     ][c]);
 }

 int main(int argc, char const *argv[]){
     ios::sync_with_stdio(false);
     cin >> n >> m;
     ; i <= n; ++i){
         cin >> a[i];
     }
     cout <<  << endl;
     createTable(floor(log(n)/log()));
     init();
     ; i <= n; ++i){
         ){
             cout << query(,i-) << endl;
         }else{
             cout << query(i-m,i-) << endl;
         }
     }
     ;
 }

快读快输部分:

 void read(int &x){
     x=;char c=getchar();
     ')c=getchar();
     '){
         x=x*+c-';
         c=getchar();
     }
 }

 void writeln(int x){
     ,len=;
     ;len++;}
     ;putchar(x/y+);x%=y;}
     putchar('\n');
 }

看来是没救了,试试线段树行不行。。。PS:题解显示是单调队列什么的,没仔细看。

Update:2018 - 06 - 06

找到st的模板题了:https://www.luogu.org/problemnew/show/P3865

用上面的代码改一下就好了,神奇的是cin/cout会超时7个点,改快读快写就A了。。

 #include <cstdio>
 #include <algorithm>
 #include <cmath>

 using namespace std;

 ],line[][],a[],n,m;

 inline int read(){
     ,w=;
     char ch=getchar();
     ;ch=getchar();}
     +ch-',ch=getchar();
     return s*w;
 }

 inline void write(int x)  {
      ) putchar('-'),x=-x;
      ) write(x/);
      putchar(x%+');
 } 

 void createTable(int size){
     powerTable[] = ;
     ; i <= size; ++i){
         powerTable[i] =  * powerTable[i-];
     }
 }

 void init(){
     ; i <= n; ++i){
         line[i][] = a[i];
     }
     ; i <= floor(log(n)/log()); ++i){
         ; j <= n-powerTable[i]+; ++j){
             line[j][i] = max(line[j][i-],line[j+powerTable[i-]][i-]);
         }
     }
 }

 int query(int head,int tail){
     )/log());
     ][c]);
 }

 int main(int argc, char const *argv[]){
     n = read();
     m = read();
     ; i <= n; ++i){
         a[i] = read();
     }
     createTable(floor(log(n)/log()));
     init();
     ;i <= m;i++){
         int a = read(), b = read();
         write(query(a, b));printf("\n");
     }
     ;
 }

其实大同小异,只改了一部分

RMQ入门的更多相关文章

  1. RMQ入门解析

    参照大佬博客:https://www.cnblogs.com/yoke/p/6949838.html RMQ(Range Minimum/Maximum Query),  是一种问题,即 查询给定区间 ...

  2. hdu 1754:I Hate It(线段树,入门题,RMQ问题)

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  3. poj 1330(RMQ&LCA入门题)

    传送门:Problem 1330 https://www.cnblogs.com/violet-acmer/p/9686774.html 参考资料: http://dongxicheng.org/st ...

  4. RMQ LAC 入门

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

  5. RMQ 算法入门

    1. 概述 RMQ(Range Minimum/Maximum Query).即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A ...

  6. RMQ 的入门 hdu1806

    RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干次询问RMQ(i,j),返回数列A中下标在区间[i,j]中的最小/大值 ...

  7. [转] POJ图论入门

    最短路问题此类问题类型不多,变形较少 POJ 2449 Remmarguts' Date(中等)http://acm.pku.edu.cn/JudgeOnline/problem?id=2449题意: ...

  8. [Luogu P1886]滑动窗口--单调队列入门

    题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. 例如: The array i ...

  9. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

随机推荐

  1. 【BZOJ1072】【SCOI2007】排列 [状压DP]

    排列 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给一个数字串s和正整数d, 统计s有多 ...

  2. 51nod 1190 最小公倍数之和 V2

    给出2个数a, b,求LCM(a,b) + LCM(a+1,b) + .. + LCM(b,b). 例如:a = 1, b = 6,1,2,3,4,5,6 同6的最小公倍数分别为6,6,6,12,30 ...

  3. Html5学习2(Html表格、Html列表、Html5新元素、Canvas (坐标、路径、画圆、文本、渐变、图像))

    Html表格 1.表格中的表头:<th></th>.其中表头部分字体加粗,颜色深绿色 <h4>水平标题:</h4> <table border=& ...

  4. 超详细的Java面试题总结(二)之Java基础知识篇

    多线程和Java虚拟机 创建线程有几种不同的方式?你喜欢哪一种?为什么? 继承Thread类 实现Runnable接口 应用程序可以使用Executor框架来创建线程池 实现Callable接口. 我 ...

  5. 制作Solaris系统的USB启动盘

    制作方法: 1. wget http://192.168.2.5/surefiler-installer/2011-12-09/devel-2011.12.9.tgz 2. cd /root tar  ...

  6. Linux-进程间通信(四): 域套接字

    1. 域套接字: (1) 只能用于同一设备上不同进程之间的通信: (2) 效率高于网络套接字.域套接字仅仅是复制数据,并不走协议栈: (3) 可靠,全双工: 2. 域套接字地址结构: struct s ...

  7. Unsupported gpu architecture 'compute_20'

    NVCC src/caffe/layers/reduction_layer.cunvcc fatal   : Unsupported gpu architecture 'compute_20'Make ...

  8. VPS性能综合测试(5):UnixBench工具介绍

    UnixBench 介绍 UnixBench 是一个类 unix (Unix, BSD, Linux 等) 系统下的性能测试工具,它是一个开源工具.可以用于测试系统主机的性能. UnixBench 进 ...

  9. Linux Python apache的cgi配置

    一.找到安装Apache的目录/usr/local/apache2/conf,并对httpd.conf配置文件进行修改 1.加载cgi模块 去掉注释: LoadModule cgid_module m ...

  10. 多路复用I/O模型epoll() 模型 代码实现

    epoll模型 int epoll_create(int maxevent) //创建一个epoll的句柄 然后maxevent表示监听的数目的大小int epoll_ctl(int epollfd, ...