[ USACO 2013 OPEN ] Photo
阿新 • • 發佈:2018-11-15
pac script 優秀 sdi esc 奶牛 usaco 哪些 continue
\(\\\)
Description
有一個長度為 \(n\) 的奶牛隊列,奶牛顏色為黑或白。
現給出 \(m\) 個區間 \([L_i,R_i]\) ,要求:每個區間裏 有且只有一只黑牛 。
問滿足所有給出限制最少有多少頭黑牛,若無合法方案輸出 \(-1\) 。
- \(n\le 2\times 10^5,m\le 10^5\)
\(\\\)
Solution
單調隊列優化。
設 \(f[i]\) 表示,第 \(i\) 個位置為黑牛, \([1,i]\) 的設置符合所有限制,最少有多少頭黑牛。
考慮合法的轉移區間的限制有哪些。
每個區間裏只能有一頭黑牛。
這個限制說明,所有包含 \(i\)
每個區間裏必須有一頭黑牛。
這個限制比較麻煩。因為不能有區間空著,所以所有不包含 \(i\) 的區間裏都要有黑牛。
所以我們要找到,不包含 \(i\) 的區間裏最大的 \(L_i\),轉移的右端點就是這個 \(L_i\) 。
然後就可以單調隊列優化了。註意不合法狀態不放到單調隊列裏。
\(\\\)
Code
寫起來其實還是可以判斷代碼能力的。
有一種比較優秀的寫法 不知道比我原來yy的高到哪裏去了 ,利用了一個單調性。
一個點轉移的合法區間左右端點其實都有單調性。
如果包含這個位置的最左端點要小於上一個位置,顯然上一個位置可以直接換成這個值。
如果不包含這個位置的最右端點要小於上一個位置,顯然這個位置的右端點也可以直接換成上一個位置的值。
這樣一來我們的預處理是線性的了,也就是說,對於每個區間,我們只需要標記區間右端點和區間右端點 \(+1\) 的位置,最後掃描一遍所有位置。
還有一個技巧是統計答案的時候,不需要討論最後一個位置什麽顏色,只需要讓數列長度 \(+1\) , 最後一個位置的 \(f\) 值就是答案。
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 200010 #define R register #define gc getchar #define inf 2000000000 using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } int n,m,l[N],r[N],f[N],q[N],hd,tl; int main(){ n=rd(); m=rd(); for(R int i=1;i<=n+1;++i) r[i]=i-1; for(R int i=1,sl,sr;i<=m;++i){ sl=rd(); sr=rd(); r[sr]=min(r[sr],sl-1); l[sr+1]=max(l[sr+1],sl); } for(R int i=1;i<=n+1;++i) l[i]=max(l[i],l[i-1]); for(R int i=n;i;--i) r[i]=min(r[i],r[i+1]); int ptr=1; hd=tl=1; for(R int i=1;i<=n+1;++i){ while(ptr<=r[i]){ if(f[ptr]<0){++ptr;continue;} while(f[ptr]>f[q[tl]]&&hd<=tl) --tl; q[++tl]=ptr; ++ptr; } while(q[hd]<l[i]&&hd<=tl) ++hd; if(hd<=tl) f[i]=f[q[hd]]+(i!=n+1); else f[i]=-1; } printf("%d\n",f[n+1]); return 0; }
[ USACO 2013 OPEN ] Photo