RMQ
Posted yiyiyizqy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RMQ相关的知识,希望对你有一定的参考价值。
总结rmq
Rmq的功能是寻找一个区间里面最大值或者最小值。
求一个区间里面最大的数可以怎么求呢。可以两个两个做比较,求出最大(小)值,从两组(四个数)之中挑取出刚刚两个数求出的最大值,再比较,就是四个数字的最大值,然后再到八个。
这样说有点抽象,下面列一个表格
i |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
2^0 |
3 |
5 |
4 |
1 |
7 |
9 |
6 |
8 |
10 |
2^1 |
5 |
5 |
4 |
7 |
9 |
9 |
8 |
10 |
10 |
2^2 |
5 |
7 |
9 |
9 |
9 |
10 |
10 |
10 |
10 |
左边第一列,是说,以i开头的2^0个数字作比较后,最大的数字是多少。
我们能从第二和第三行发现,比较时,只要取出i上一层的值,和上一层加2^(L-1)个数字,一比较,既是2^L中的最大值。
因此可以用f[i][L]数组记录,从第i个为起点,连续2^L个数的最大值。所以,可以得出规律,f[i][L]=max(f[i][l-1],f[i+2^(L-1)][L-1])。但是要注意,编代码时不可能写成2^L的格式,应该用1<<L。
求最小值时,把max改成min就可以了。
但是,我们这样只求出了个数为1、2、4、8、16……时的最大(小)值,那么如果是单数时怎么办呢,我当时的第一反应是补,但是事实证明,还有更优的方法。
当给出7个数字:1 3 5 7 9 11 13 时,我们无妨可以把它分为四个一组,1 3 5 7一组,7 9 11 13一组,这样子就可以不漏掉。如果是分为两个一组呢?那是不够的。
I |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
K |
1 |
1 |
2 |
2 |
4 |
4 |
4 |
8 |
8 |
P |
0 |
0 |
1 |
1 |
2 |
2 |
2 |
3 |
3 |
第一行记录的是个数为i时。第二行是所需要拆分成的组数,最后一行是把第二行的组数转化为2^x组中的x,可以节省很多数组空间,上面的f数组中的L也作了同样的优化。
接下来,找规律的时候就到了。我们会发现,当前面第i-1个的K*2小于i时,ki=ki-1*2。也就是pi=pi-1++。算出p也就是做好预处理。
最后的答案
long=b-a+1; ma=max(f[a][p[long]],f[b-2^k+1][k])。
int main() { freopen("1656.in","r",stdin); freopen("1656.out","w",stdout); cin>>n>>q; for(int i=1;i<=n;i++) { cin>>a; f[i][0]=a; g[i][0]=a; } p[1]=0; for(int i=2;i<=n;i++) { if((1<<p[i-1]+1)<i) p[i]=p[i-1]+1; else p[i]=p[i-1]; } for(int l=1;(1<<l)<n;l++) { for(int i=1;i<=n;i++) { f[i][l]=f[i][l-1]; g[i][l]=g[i][l-1]; if(i+(1<<(l-1))<=n) { f[i][l]=max(f[i][l],f[i+(1<<(l-1))][l-1]); g[i][l]=min(g[i][l],g[i+(1<<(l-1))][l-1]); } } } while(q) { q--; cin>>a>>b; int lo=b-a+1; int k=p[lo]; int mi=min(g[a][k],g[b-(1<<k)+1][k]); int ma=max(f[a][k],f[b-(1<<k)+1][k]); int ans=ma-mi; cout<<ans<<endl; } return 0; }
以上是关于RMQ的主要内容,如果未能解决你的问题,请参考以下文章