【[POI2015]WIL-Wilcze doły】
阿新 • • 發佈:2019-01-02
第一篇題解確實會被討論區裡的資料hack掉,那麼就隨便水一個不會被hack掉的題解吧
首先我們嘗試著發現這道題的一些結論,你就會發現答案是單調的不降的
這裡的答案不降指的是選擇每一個位置\(i\)作為結尾能形成的最長區間的左端點是單調不降的,這個很好證明,將\(i-1\)這個位置作為結尾形成的最長區間的左端點不可能比\(i\)作為結尾形成的最長區間的左端點更靠右
如果更靠右的話,那麼\(i-1\)形成的區間還能更靠左一些,這與我們的假設不符,所以這個結論是成立的
之後我們就可以利用這個結論計算每一個\(i\)為結尾的區間的左端點在哪裡了
由於\(i\)的左端點不可能比\(i-1\)的更靠左,所以我們就直接來將\(i-1\)
如果\(p[i]-p[last-1]\)即這段區間的和減去這個區間內所有長度為\(d\)的區間和的最大值還是超過\(p\),那麼我們就讓\(last++\),直到滿足條件為止
至於怎麼維護一個區間內所有長度為\(d\)的區間和的最大值,我們用一個單調佇列就好了
時間複雜度其實是均攤了兩次,但是還是非常優秀的\(O(n)\)
程式碼
#include<iostream> #include<cstring> #include<queue> #include<cstdio> #define re register #define maxn 2000005 #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define LL unsigned long long LL n,p,d; LL a[maxn],pre[maxn]; LL ans,t[maxn],last; inline LL read() { LL x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return x; } int main() { n=read(); p=read(); d=read(); for(re int i=1;i<=n;i++) a[i]=read(); for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i]; std::deque<int> q; for(re int i=d;i<=n;i++) t[i]=pre[i]-pre[i-d];//t[i]表示[i-d+1,i]這個區間的和 ans=d;//最開始ans為d q.push_back(d); last=1; for(re int i=d+1;i<=n;i++) { while(!q.empty()&&t[i]>t[q.back()]) q.pop_back(); q.push_back(i);//在隊尾加入一個元素 while(!q.empty()&&q.front()-d+1<last) q.pop_front(); //,如果隊首元素的左端點比last還小,那麼就彈出不合法的隊首元素 while(!q.empty()&&pre[i]-pre[last-1]-t[q.front()]>p) { last++; while(!q.empty()&&q.front()-d+1<last) q.pop_front(); //last++後也要維護隊首的合法性 } ans=max(ans,i-last+1); } std::cout<<ans; return 0; }