P1440 求m區間內的最小值 題解

博客園同步

原題鏈接

簡要題意:

給定 nn 個數 {ai}\{a_i\} 和一個 mm,輸出所有 1in1 \leq i \leq nminmax(1,im+1)iai\min_{\max(1, i-m+1)}^{i} a_i.

n,m2×106n,m \leq 2 \times 10^6.

顯然這就是 對每個數求出其前 mm 個數的最小值

樸素令 fi=minmax(1,im+1)iaif_i = \min_{\max(1, i-m+1)}^{i} a_iO(n2)\mathcal{O}(n^2) 炸的飛起。

當然你用線段樹,樹狀數組,RMQ\text{RMQ} 來維護區間最小都是 O(nlogn)\mathcal{O}(n \log n) 的,不夠優,常數還不一定能過。

我們考慮如何從 [l,r][l+1,r+1][l,r] \rightarrow [l+1,r+1] 即可。

用一個隊列 qq,維護當前 可能會影響答案的編號

什麼叫做 可能會影響答案

比方說當前求的是編號 ii 的答案,那麼顯然,編號 u<im+1u<i-m+1 就應該出隊,因爲 所以包括 ii 在內之後的節點都不可能用到編號 uu 來作爲決策了

其次,如果存在 uu 使得 u<i,au>aiu < i , a_u > a_i,這時候你會發現,ii 節點的價值比 uu 更高。因爲,ii 節點及以後的點用 ii 的次數比用 uu 的次數多,而 ii 的決策又始終比 uu,那麼 uu 就可以出隊了。

我們把當前區間的最小值編號始終放在第一位,以便取出。

時間複雜度: O(n)\mathcal{O}(n).(每個點入隊一次,出隊一次)

實際得分:100pts100pts.

#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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章