【专题】用ST表解决RMQ刷题总结

看了一下上次写博客居然是好久以前的事了(我真是老懒狗了 )
开门见山,直接放专题链接和代码

kuangbin rmq专题

这个contest里面一共十道题但是实际上有2道dp的题混入其中,还有一道水题不用rmq就能做(不过做上头了也能强行rmq),还有一道HYSBZ上面的题因为懒就没有做。。。 然后就剩下6道题了,一道一道按难易和类型(一维rmq/二维rmq)摆代码

一维rmq(4道):

POJ3264

题意略。。。就是rmq板子题,有手就行

//POJ3264
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn=5e4+5;int mx[maxn][17],mi[maxn][17];
int a[maxn],n,q;void rmqinit(){for(int i=1;i<=n;i++) mx[i][0]=mi[i][0]=a[i];for(int j=1;j<=16;j++)for(int i=1;i+(1<<j)-1<=n;i++)mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]),mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}int rmq(int l,int r){int d=log2(r-l+1);int maxx=max(mx[l][d],mx[r-(1<<d)+1][d]);int minn=min(mi[l][d],mi[r-(1<<d)+1][d]);return maxx-minn;
}int main()
{scanf("%d%d",&n,&q);for(int i=1;i<=n;i++) scanf("%d",&a[i]);rmqinit();while(q--){int l,r;scanf("%d%d",&l,&r);printf("%d\n",rmq(l,r));}
}

POJ3368

题意:给你一个数列,有n个数a1,a2,….,an,并且是不减的,满足ai <= ai+1(1<=i<n)。现有m次询问,每个询问的结果为[l,r]区间内出现次数最多的数出现了多少次。

思路:无脑线段树维护区间最多和左右值及左右值出现次数 (既然是rmq专题,当然是rmq了) 由于数列有序是不减的所以相同的值在一起,用数组b[i]记录当前值a[i]是第几个出现的相同值,然后就可以rmq查询最大出现次数了,但是这样有一个问题就是如果查到的值是问询区间左端,前面有一部分不再区间里,所以再开一个c[]数组,倒着扫一下,记录当前与每个a[i]相同值的连续序列的右边界是什么,在查询的时候直接分左端l到c[l],再从c[l]+1到r进行rmq最后二者取max即可

//POJ3368
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1e5+5;int f[maxn][18],a[maxn],b[maxn],c[maxn],n,q;void rmqinit(){for(int i=1;i<=n;i++) f[i][0]=b[i];for(int j=1;j<=17;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]);
}int rmq(int l,int r){if(l>r) return 0;int d=log2(r-l+1);return max(f[l][d],f[r-(1<<d)+1][d]);
}int main()
{while(~scanf("%d",&n)&&n){scanf("%d",&q);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=n;i++){if(i==1||a[i]!=a[i-1]) b[i]=1;else b[i]=b[i-1]+1;}int rbound=n;for(int i=n;i>0;i--){if(i==n||b[i]==b[i+1]-1) c[i]=rbound; else c[i]=rbound=i;}rmqinit();while(q--){int l,r;scanf("%d%d",&l,&r);int ans1=min(c[l],r)-l+1;int ans2=rmq(min(c[l],r)+1,r);printf("%d\n",max(ans1,ans2));}}
}

HDU3183

题意:给一串数长度为n,让你从中删m个数,使得剩下的数刨去前导零之后最小

思路:这一上来当然想到的是无脑贪心啊,考虑只删一个数,最优是从前往后扫,当遇到一个相邻递减的数对,就把前面那个大的删掉,这个题的数据范围暴力也是可以过的,但是!rmq怎么做呢???看了别人的思路之后才懂,答案是n-m位数(保留前导零的话),先rmq找1到(m+1)里面最小的数(第id个),这个数肯定是答案的第一位,然后再找id+1到m+2最小的,这个是答案的第二位。。。。以此类推。
对了rmq维护的是区间最小值的下标,不是值,不想用三目运算符,所以就重载了min函数。。。
坑点:如果都删的话最后要输出0,不是空,wa了好久。。。

//HDU3183
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1e3+5;char ch[maxn];
int m,n,f[maxn][11],ans[maxn];inline int min(int x,int y){if(ch[x]==ch[y]) return x<y?x:y;return ch[x]<ch[y]?x:y;
}void rmqinit(){for(int i=1;i<=n;i++) f[i][0]=i;for(int j=1;j<=10;j++)for(int i=1;i+(1<<j)-1<=n;i++)f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}int rmq(int l,int r){int d=log2(r-l+1);return min(f[l][d],f[r-(1<<d)+1][d]);
}int main()
{while(~scanf("%s%d",ch+1,&m)){n=strlen(ch+1);rmqinit();int l=0,r=m+1,cnt=0;while(r<=n){l=rmq(l+1,r);r++;ans[++cnt]=l;}if(m==n){puts("0");continue;}int st=1;while(ch[ans[st]]=='0'&&st<cnt) st++; for(int i=st;i<=cnt;i++) printf("%c",ch[ans[i]]);puts("");}
}

HDU3486

题意:给n个数,求最小的段数,使得每一段的最大值之和大于给定的k。每一段的长度相等,最后若干个丢掉。
思路:这题要求最少的区间数使得所有区间的最大值之和大于k,先预处理每个区间的最大值,然后枚举区间个数(二分理论上不对,但是能AC,emmm),计算和是否大于k,最小的区间数就是答案

//HDU3486
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=2e5+5;int n,k,a[maxn],f[maxn][19];void rmqinit(){for(int i=1;i<=n;i++) f[i][0]=a[i];for(int j=1;j<=18;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]);
}int rmq(int l,int r){int d=log2(r-l+1);return max(f[l][d],f[r-(1<<d)+1][d]);
}int main()
{while(~scanf("%d%d",&n,&k)){if(n<0&&k<0) break;int sum=0,maxx=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum+=a[i],maxx=max(maxx,a[i]);}if(sum<=k){puts("-1");continue;}rmqinit();int i;for(i=max(1,(k+1)/maxx);i<=n;i++){int len=n/i;int tot=0;for(int j=1;j<=i;j++){tot+=rmq((j-1)*len+1,j*len);if(tot>k) break;}if(tot>k) break;}printf("%d\n",i);}
}

二维rmq(2道):

其实二维rmq和一维rmq的思想差不多,用于解决二维区间最值问题,有两种不同的实现方法

法一:1) O(nmlog(m))预处理----O(n)查询,把每一行都当成一维RMQ处理

法二:2) O(nmlog(n)*log(m)预处理----O(1)查询,f[ i ][ j ][ k ][ l ]表示从第一个点也就是左上角(i,j)起,到右下角(i+2^k, j+2^l)但是不包括右下角和右列和下行 ,然后预处理和查询的时候都把大矩形分成四个小矩形。

【注意】预处理的时候除了和一维rmq一样处理f[i][j][0][0],同时千万不要忘了和一维一样的方法处理f[i][j][0][x]和f[i][j][x][0]的情形。

POJ2019

题意:没什么好说的了,就是板子题,只不过问询区间是正方形边长是固定的b,给左上角坐标查询最大与最小的差

//POJ2019
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=255;int n,b,k;
int mx[maxn][maxn][9][9],mi[maxn][maxn][9][9];
int a[maxn][maxn];void rmqinit(){for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) mx[i][j][0][0]=mi[i][j][0][0]=a[i][j];for(int j=1;j<=n;j++)for(int k1=1;k1<=8;k1++)for(int i=1;i+(1<<k1)-1<=n;i++)mx[i][j][k1][0]=max(mx[i][j][k1-1][0],mx[i+(1<<(k1-1))][j][k1-1][0]),mi[i][j][k1][0]=min(mi[i][j][k1-1][0],mi[i+(1<<(k1-1))][j][k1-1][0]);for(int i=1;i<=n;i++)for(int k2=1;k2<=8;k2++)for(int j=1;j+(1<<k2)-1<=n;j++)mx[i][j][0][k2]=max(mx[i][j][0][k2-1],mx[i][j+(1<<(k2-1))][0][k2-1]),mi[i][j][0][k2]=min(mi[i][j][0][k2-1],mi[i][j+(1<<(k2-1))][0][k2-1]);for(int k1=1;k1<=8;k1++)for(int k2=1;k2<=8;k2++)    for(int i=1;i+(1<<k1)-1<=n;i++)for(int j=1;j+(1<<k2)-1<=n;j++)mx[i][j][k1][k2]=max(max(mx[i][j][k1-1][k2-1],mx[i+(1<<(k1-1))][j][k1-1][k2-1]),max(mx[i][j+(1<<(k2-1))][k1-1][k2-1],mx[i+(1<<(k1-1))][j+(1<<(k2-1))][k1-1][k2-1])),mi[i][j][k1][k2]=min(min(mi[i][j][k1-1][k2-1],mi[i+(1<<(k1-1))][j][k1-1][k2-1]),min(mi[i][j+(1<<(k2-1))][k1-1][k2-1],mi[i+(1<<(k1-1))][j+(1<<(k2-1))][k1-1][k2-1]));
}int rmq(int x1,int y1,int x2,int y2){int d1=log2(x2-x1+1);int d2=log2(y2-y1+1);int maxx=max(max(mx[x1][y1][d1][d2],mx[x2-(1<<d1)+1][y1][d1][d2]),max(mx[x1][y2-(1<<d2)+1][d1][d2],mx[x2-(1<<d1)+1][y2-(1<<d2)+1][d1][d2]));int minn=min(min(mi[x1][y1][d1][d2],mi[x2-(1<<d1)+1][y1][d1][d2]),min(mi[x1][y2-(1<<d2)+1][d1][d2],mi[x2-(1<<d1)+1][y2-(1<<d2)+1][d1][d2]));return maxx-minn;
}int main()
{scanf("%d%d%d",&n,&b,&k);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);rmqinit();while(k--){int r,c;scanf("%d%d",&r,&c);printf("%d\n",rmq(r,c,r+b-1,c+b-1));}
}

HDU2888

题意:给定一个n * m的矩阵,再给定q个询问,每次询问(r1,c1)为左上角,(r2,c2)为右下角的子矩形的最大值,并且判断该最大值是否出现在了这个子矩阵的4个顶角上
思路:也是板子题。。。

//HDU2888
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=305;int m,n,q;
int mx[maxn][maxn][9][9];void rmqinit(){for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) scanf("%d",&mx[i][j][0][0]);for(int j=1;j<=m;j++)for(int k1=1;k1<=8;k1++)for(int i=1;i+(1<<k1)-1<=n;i++)mx[i][j][k1][0]=max(mx[i][j][k1-1][0],mx[i+(1<<(k1-1))][j][k1-1][0]);for(int i=1;i<=n;i++)for(int k2=1;k2<=8;k2++)for(int j=1;j+(1<<k2)-1<=m;j++)mx[i][j][0][k2]=max(mx[i][j][0][k2-1],mx[i][j+(1<<(k2-1))][0][k2-1]);for(int k1=1;k1<=8;k1++)for(int k2=1;k2<=8;k2++) for(int i=1;i+(1<<k1)-1<=n;i++)for(int j=1;j+(1<<k2)-1<=m;j++)mx[i][j][k1][k2]=max(max(mx[i][j][k1-1][k2-1],mx[i+(1<<(k1-1))][j][k1-1][k2-1]),max(mx[i][j+(1<<(k2-1))][k1-1][k2-1],mx[i+(1<<(k1-1))][j+(1<<(k2-1))][k1-1][k2-1]));
}int rmq(int x1,int y1,int x2,int y2){int d1=log2(x2-x1+1);int d2=log2(y2-y1+1);return max(max(mx[x1][y1][d1][d2],mx[x2-(1<<d1)+1][y1][d1][d2]),max(mx[x1][y2-(1<<d2)+1][d1][d2],mx[x2-(1<<d1)+1][y2-(1<<d2)+1][d1][d2]));
}int main()
{while(~scanf("%d%d",&n,&m)){rmqinit();for(scanf("%d",&q);q;q--){int r1,c1,r2,c2;scanf("%d%d%d%d",&r1,&c1,&r2,&c2);int maxx=rmq(r1,c1,r2,c2);printf("%d ",maxx);if(mx[r1][c1][0][0]==maxx||mx[r1][c2][0][0]==maxx||mx[r2][c1][0][0]==maxx||mx[r2][c2][0][0]==maxx) puts("yes");else puts("no");}       }
}

【专题】用ST表解决RMQ刷题总结相关推荐

  1. 线性结构 —— ST 表与 RMQ

    [概述] RMQ(Range Minimum/Maximum Query),是对于长度为 n 的数列 A,回答若干次询问 RMQ(i,j),返回数列 A 中下标在区间 [i,j] 中的最值,即:区间最 ...

  2. ST算法解决RMQ问题

    关于ST算法,实际上它本身并不难,它的思想是动态规划.主要用来求RMQ问题,时间复杂度为O(NlgN+M)  关于RMQ问题描述: 输入N个数和M次询问,每次询问一个区间[L,R],求第L个数到R个数 ...

  3. 哈希表——算法专项刷题(五)

    五.哈希表 哈希表多用于辅助记录是否存在key或者通过key找下标value 5.1插入.删除和随机访问都是O(1)的容器 原题链接 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结 ...

  4. ST函数(ST表)RMQ O(1)查询 离线

    ST算法是基于倍增的动态规划算法. #include<iostream> #include<cstdio> #include<cstdlib> #include&l ...

  5. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  6. 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析

    目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...

  7. C#LeetCode刷题-哈希表

    哈希表篇 # 题名 刷题 通过率 难度 1 两数之和 C#LeetCode刷题之#1-两数之和(Two Sum) 42.8% 简单 3 无重复字符的最长子串   24.2% 中等 18 四数之和   ...

  8. 数据结构与算法一年刷题特训营

    [福利]C++语言基础       1.1 头文件.输入输出.格式.基本运算.mp4       2.1 if.swith语句.逻辑运算.mp4       3.1 for . break . con ...

  9. ZJOI2019一轮停课刷题记录

    Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...

最新文章

  1. java中使用递归方法删除_删除和拷贝文件递归方法(Java实现)
  2. PHP中text里数字相加,excel文字数字如何混合求和
  3. win10安装vmware tools + 无法拖拽文件解决
  4. EFI格式linux启动u盘,制作BIOS和EFI多启动U盘
  5. php域名墙检测,php 网站域名被墙判断请求方法
  6. Resource Monitor的使用和理解
  7. python3.6字典有序_为什么Python3.6字典变得有序了?
  8. 为什么软件开发方法论让你觉得糟糕?
  9. 解决应用程序无法正常启动0xc0150002等问题
  10. QtCreator-----Kits选项选择
  11. 几种常用的文件加密方法
  12. XenApp发布IE为默认最大化
  13. PDF文件如何删除页面
  14. PBFT -Golang实现详解
  15. 把Excel批注的“红三角”放在单元格左上角_干货!《跟王佩丰学Excel教程》笔记...
  16. 2020年9月电子学会Python等级考试试卷(四级)考题解析
  17. 说到做到,贴个70后男程序员的成长经历
  18. 老年护理虚拟仿真解决方案
  19. 大数据技术原理与应用(复习)
  20. Nginx学习笔记(三):负载均衡

热门文章

  1. 基于springboot的工资管理系统
  2. PS人物眼球制作3 - 眼球浑浊感和血丝感制作
  3. 01串状态压缩(位运算)
  4. c语言中如何表示特殊字符星星,C语言如何用代码打出星星,如下知道
  5. 教你用PixiJs实现复杂动画
  6. 查找过的资料-防丢失
  7. activiti入门系列文章9 - 委派与转办
  8. 将D盘多余空间分配给C盘
  9. JavaScript的逆袭
  10. window10离线安装net3.5的三种方法