【专题】用ST表解决RMQ刷题总结
【专题】用ST表解决RMQ刷题总结
看了一下上次写博客居然是好久以前的事了(我真是老懒狗了 )
开门见山,直接放专题链接和代码
kuangbin 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刷题总结相关推荐
- 线性结构 —— ST 表与 RMQ
[概述] RMQ(Range Minimum/Maximum Query),是对于长度为 n 的数列 A,回答若干次询问 RMQ(i,j),返回数列 A 中下标在区间 [i,j] 中的最值,即:区间最 ...
- ST算法解决RMQ问题
关于ST算法,实际上它本身并不难,它的思想是动态规划.主要用来求RMQ问题,时间复杂度为O(NlgN+M) 关于RMQ问题描述: 输入N个数和M次询问,每次询问一个区间[L,R],求第L个数到R个数 ...
- 哈希表——算法专项刷题(五)
五.哈希表 哈希表多用于辅助记录是否存在key或者通过key找下标value 5.1插入.删除和随机访问都是O(1)的容器 原题链接 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结 ...
- ST函数(ST表)RMQ O(1)查询 离线
ST算法是基于倍增的动态规划算法. #include<iostream> #include<cstdio> #include<cstdlib> #include&l ...
- 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析
目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...
- 倍增算法入门 超详细解答+LCA+RMQ(ST表)+例题剖析
目录 一.倍增算法 二.倍增算法的应用:求LCA(最近公共祖先)附模板题 三.倍增算法的应用:RMQ 问题(ST表)附模板题 一.倍增算法 要了解倍增之前,强烈建议大家先看一下这位大佬对倍增的解释:[ ...
- C#LeetCode刷题-哈希表
哈希表篇 # 题名 刷题 通过率 难度 1 两数之和 C#LeetCode刷题之#1-两数之和(Two Sum) 42.8% 简单 3 无重复字符的最长子串 24.2% 中等 18 四数之和 ...
- 数据结构与算法一年刷题特训营
[福利]C++语言基础 1.1 头文件.输入输出.格式.基本运算.mp4 2.1 if.swith语句.逻辑运算.mp4 3.1 for . break . con ...
- ZJOI2019一轮停课刷题记录
Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...
最新文章
- java中使用递归方法删除_删除和拷贝文件递归方法(Java实现)
- PHP中text里数字相加,excel文字数字如何混合求和
- win10安装vmware tools + 无法拖拽文件解决
- EFI格式linux启动u盘,制作BIOS和EFI多启动U盘
- php域名墙检测,php 网站域名被墙判断请求方法
- Resource Monitor的使用和理解
- python3.6字典有序_为什么Python3.6字典变得有序了?
- 为什么软件开发方法论让你觉得糟糕?
- 解决应用程序无法正常启动0xc0150002等问题
- QtCreator-----Kits选项选择
- 几种常用的文件加密方法
- XenApp发布IE为默认最大化
- PDF文件如何删除页面
- PBFT -Golang实现详解
- 把Excel批注的“红三角”放在单元格左上角_干货!《跟王佩丰学Excel教程》笔记...
- 2020年9月电子学会Python等级考试试卷(四级)考题解析
- 说到做到,贴个70后男程序员的成长经历
- 老年护理虚拟仿真解决方案
- 大数据技术原理与应用(复习)
- Nginx学习笔记(三):负载均衡