簡要題意:
給定 個數 和一個 ,輸出所有 的 .
.
顯然這就是 對每個數求出其前 個數的最小值。
樸素令 , 炸的飛起。
當然你用線段樹,樹狀數組, 來維護區間最小都是 的,不夠優,常數還不一定能過。
我們考慮如何從 即可。
用一個隊列 ,維護當前 可能會影響答案的編號。
什麼叫做 可能會影響答案?
比方說當前求的是編號 的答案,那麼顯然,編號 就應該出隊,因爲 所以包括 在內之後的節點都不可能用到編號 來作爲決策了。
其次,如果存在 使得 ,這時候你會發現, 節點的價值比 更高。因爲, 節點及以後的點用 的次數比用 的次數多,而 的決策又始終比 優,那麼 就可以出隊了。
我們把當前區間的最小值編號始終放在第一位,以便取出。
時間複雜度: .(每個點入隊一次,出隊一次)
實際得分:.
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+1;
inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
inline void write(int x) {
if(x<0) {putchar('-');write(-x);return;}
if(x<10) {putchar(char(x%10+'0'));return;}
write(x/10);putchar(char(x%10+'0'));
}
int n,m,a[N],q[N];
int l=1,r=0;
int main() {
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) {
printf("%d\n",a[q[l]]);
while(i-q[l]+1>m && l<=r) l++; //無效節點
while(a[i]<a[q[r]] && l<=r) r--; //i 比當前節點更優
q[++r]=i; //i 入隊作爲一個新的決策
}
return 0;
}