洛谷3594 [POI2015]WIL-Wilcze doły(單調佇列)
阿新 • • 發佈:2018-11-11
題目
給定一個長度為n的序列,你有一次機會選中一段連續的長度不超過d的區間,將裡面所有數字全部修改為0。請找到最長的一段連續區間,使得該區間內所有數字之和不超過p。
特性
選擇一個區間[i,i+d-1],那麼我們選擇的最長區間一定在這個區間附近,也就是連續的。
題解
單調佇列
很明顯是一個O(N)的演算法,那麼時間只夠我們列舉一個右端點。
列舉右端點後,最優的左端點在哪裡呢?很明顯,這個左端點隨著右端點的右移,它也會隨之右移或者原地不動。
我們選的數 = 區間和 - 區間一段長度為d的區間
我們要維護的就是區間內一段長度為d的最大和區間,需要維護一個單調遞減的佇列。
當選的數大於p時,就要縮短區間,同時彈出起點超出last的長度為d的區間。
程式碼
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=2000010; inline ll read() { ll re=0;char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9') re=re*10+(ch^48),ch=getchar(); return re; } int n=read(),p=read();ll d=read(); ll s[maxn],t[maxn];//t記錄長度為d的一段區間的和 int l,r,q[maxn]; int main() { for(int i=1;i<=n;i++) s[i]=s[i-1]+read(); for(int i=d;i<=n;i++) t[i]=s[i]-s[i-d];//debug i:1~n-d+1 l=r=0;q[0]=d; int ans=d,last=1; for(int i=d+1;i<=n;i++) { while(l<=r && t[q[r]]<=t[i]) r--; q[++r]=i; // while(l<=r && q[l]-d+1<last) l++; while(s[i]-s[last-1]-t[q[l]]>p) { last++; if(l<=r && q[l]-d+1<last) l++;//debug q[l]-d+1才是開頭 } ans=max(ans,i-last+1); } printf("%d\n",ans); return 0; }