P1816 忠诚 ST表模版
寒假学了ST表之后,一直没有写相关的题目,ST表的写法都快忘了,今天复习的时候,就做了几个题,来复习一下ST表。
我不知道为什么题解里都用线段树来做,线段树不但慢,还不好写。这道题分明没有修改操作,用ST表离线处理之后用O(1)的复杂度来查询就能A了。
ST表就是一个用来解决rmq(区间最值)问题的算法。ST表不支持在线修改。预处理时间复杂度O(nlogn),查询时间O(1)。
ST表需要用到DP和倍增的思想,先建立一个二维数组st[i][j],表示从第i个元素开始的2的j次方个数的最小值。
举个例子
1 2 3 4 5 五个数
st[2][2] 就是2 3 4 5这四个数中的最小值
st[3][0]就是3这一个数中的最小值
弄清楚st数组所代表的意义后,先把怎样求出st数组放在一边,来看一下如何利用st数组求解区间最值(RMQ)问题。
对于一个询问,会有一个左端点,一个右端点,因为st表所表示的范围是不断倍增的,不一定有元素正好覆盖查询区间,就需要用两个区间覆盖查询区间,因为是最值问题,所以两个区间有交叉部分并不会影响结果,只需取一个k,使得2的k次方小于区间长度(右端点-左端点+1),并且使k尽可能的大。
然后在st表中取两个元素,第一个元素的起始位置使区间的左端点,第二个元素的结束位置是区间的右端点,然后在在两个元素中取最小值。这个方法的正确性和实现方法都是显而易见的,结合代码都能理解,这里就不再赘述了。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,st[100001][21],ans[100001],a[100000],f1,f2,kkk; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); st[i][0]=a[i]; } for(int j=1;(1<<j)<=n;j++) for(int i=1;i<=n-(1<<j)+1;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); for(int i=1;i<=m;i++) { scanf("%d%d",&f1,&f2); kkk=0; while((1<<(kkk+1))<=(f2-f1+1))kkk++; ans[i]=min(st[f1][kkk],st[f2-(1<<kkk)+1][kkk]); } for(int i=1;i<=m;i++) printf("%d ",ans[i]); return 0; }