板子
以poj3264为例,mx[i][j]代表下标从i开始的连续区间长度为的最大值,mn[i][j]为最小值
ST表
#include<iostream>
using namespace std;
const int N=1e6+10,M=22;
int n,q,l,r,v;
int mx[N][M],mn[N][M],lg[N];
void init(){
for(int i=2;i<N;++i){
lg[i]=lg[i>>1]+1;
}
}
void ST(int n){
for(int len=1;(1<<len)<=n;++len){
for(int i=1;i+(1<<len)-1<=n;++i){
mx[i][len]=max(mx[i][len-1],mx[i+(1<<(len-1))][len-1]);
mn[i][len]=min(mn[i][len-1],mn[i+(1<<(len-1))][len-1]);
}
}
}
int RMQ(int l,int r,int op){
int k=lg[r-l+1];
int ans;
if(!op)ans=max(mx[l][k],mx[r-(1<<k)+1][k]);
else ans=min(mn[l][k],mn[r-(1<<k)+1][k]);
return ans;
}
int main(){
init();
while(~scanf("%d%d",&n,&q)){
for(int i=1;i<=n;++i){
scanf("%d",&v);
mx[i][0]=mn[i][0]=v;
}
ST(n);
for(int i=1;i<=q;++i){
scanf("%d%d",&l,&r);
printf("%d\n",RMQ(l,r,0)-RMQ(l,r,1));
}
}
return 0;
}
总结
个人觉得博客的这个粘代码背景没我的黑底白字好看QAQ
总结一波,代码总是越敲越熟的嘛……知识沉积的过程……
到时候改一改,传参的时候把数组名一起传进去,就能当小黑盒用了
法一(单调队列,O(n)):
//区间连续k个的最小 传进去下标
deque<int>q;
for(int i=1;i<k;++i)
{
while(!q.empty()&&high[q.back()]>high[i])q.pop_back();
q.push_back(i);
}
for(int i=k;i<n;++i)
{
while(!q.empty()&&high[q.back()]>high[i])q.pop_back();
q.push_back(i);
ans=max(ans,high[q.front()]);
if(q.front()<=i-k+1)q.pop_front();
}
法二(multiset,O(nlogn)):
这个东西和set其实没啥区别,
允许数字被重复插入,操作差不太多
//区间连续k个的最小 传进去值
multiset<int>q;
for(int i=1;i<k;++i)q.insert(high[i]);
for(int i=k;i<n;++i)
{
q.insert(high[i]);
ans=max(ans,*q.begin());
q.erase(q.find(high[i-k+1]));
}
法三(ST表RMQ,预处理O(nlogn),查询O(1)):
void ST(int n)
{
for(int i=1;i<n;++i)dp[i][0]=high[i];
for(int j=1;(1<<j)<n;++j)
{
for(int i=1;i+(1<<j)-1<n;++i)
{
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ(int l,int r)
{
int k=log(r-l+1)/log(2);
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
ST(n);
for(int i=1;i+k-1<n;++i)
ans=max(ans,RMQ(i,i+k-1));
法四(线段树,预处理O(nlogn),查询O(logn))
挺好写的吧,基础操作……
有前面那么优秀的几种就不写这个了QAQ