传送门
Solution
- 单调队列就是一个答案的集合,维护一个单调队列就是将有可能成为答案的数加入队列,将不可能成为答案的数移出或不加入队列。这样的好处是可以
O(1)
的从队头取出答案,否则需要在“窗口”的范围内枚举答案,妥妥超时
- 假设我们将数放在数组a中,设其中有两个数满足:
i<j
&&
a[i]<a[j]
,那么在一个窗口范围内计算最大值时,
a[i]
就是无用的,即如果一个数比另一个数小,而且它的位置更靠前,那它一点前途也没有,既不可能成为当前窗口的最大值,窗口移动后的竞争力也很差,因此直接将它移出队列。
- 经过这样维护的队列,一定是单调递减的,队头即为最大值
- 计算最小值:将原数列每个数乘-1,做一遍最大值
Code
// by spli
using namespace std;
const int N=2000010;
int n,k;
int a[N],q[N],L,R;
void work(int *a,int t){
L=1;R=0;//
for(int i=1;i<=n;++i){
while(L<=R&&a[i]>=a[q[R]]) R--;
q[++R]=i;
if(q[R]-k+1>q[L]) L++;
if(i>=k) printf("%d ",a[q[L]]*t);
}
cout<<endl;
return;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
if(k==1){//特判k==1的情况,可能不需要
for(int i=1;i<=n;++i) printf("%d ",a[i]);
cout<<endl;
for(int i=1;i<=n;++i) printf("%d ",a[i]);
return 0;
}
for(int i=1;i<=n;++i) a[i]=-a[i];
work(a,-1);
for(int i=1;i<=n;++i) a[i]=-a[i];
work(a,1);
return 0;
}