1. 程式人生 > >BZOJ 3126 [USACO2013 Open]Photo (單調佇列優化DP)

BZOJ 3126 [USACO2013 Open]Photo (單調佇列優化DP)

題目大意:給你一個長度為$n$的序列和$m$個區間,每個區間內有且僅有一個1,其它數必須是0,求整個序列中數字1最多的數量

神題,竟然是$DP$

定義$f_{i}$表示第i位放一個1時,最多的1的數量

因為每個區間至少一個點,如果要在$i$位置放一個1,顯然在$i$左側沒覆蓋$i$的區間中,選擇一個位置$j$,$j$必須保證如果在$j$放一個1,那麼它右側沒有空區間再需要放1,顯然$j$是沒覆蓋i的區間中最大的左端點,維護一個數組$l_{i}$,表示最左端能轉移的區間

因為每個區間至多一個點,如果要在$i$位置放一個1,顯然在$i$左側覆蓋了$i$的區間中,選擇一個位置$j$,$j$必須保證和$i$不處於任何一個相同的區間內,顯然$j$是覆蓋了

$i$的所有區間中最小的左端點-1,維護一個數組$r_{i}$,表示最右端能轉移的區間

$f_{i}=max{f_{j},j\in[l_{i},r_{i}]}$

發現$l_{i}$和$r_{i}$都具有單調遞增的性質,用單調佇列優化即可,當然也可以用線段樹

細節比較多,建議不要看程式碼自己思考

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 200100
 5 #define ll long long 
 6 #define inf 0x3f3f3f3f
 7
using namespace std; 8 9 int gint() 10 { 11 int ret=0,fh=1;char c=getchar(); 12 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 13 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 14 return ret*fh; 15 } 16 int n,m; 17 int f[N1],l[N1],r[N1]; 18 int que[N1];
19 struct node{int l,r;}a[N1]; 20 int cmp1(node s1,node s2){ 21 if(s1.r!=s2.r) return s1.r<s2.r; 22 else return s1.l>s2.l;} 23 int cmp2(node s1,node s2){ 24 if(s1.r!=s2.r) return s1.r>s2.r; 25 else return s1.l<s2.l;} 26 27 28 int main() 29 { 30 scanf("%d%d",&n,&m); 31 for(int i=1;i<=m;i++) 32 a[i].l=gint(),a[i].r=gint(); 33 sort(a+1,a+m+1,cmp2); 34 int k=1,mi=n+1; 35 for(int i=n+1;i>=1;i--){ 36 while(k<=m){ 37 if(a[k].r>=i) 38 mi=min(mi,a[k].l),k++; 39 else break;} 40 if(i<=mi) mi=i; 41 r[i]=mi-1; 42 } 43 sort(a+1,a+m+1,cmp1); 44 int ma=0;k=1; 45 for(int i=1;i<=n+1;i++){ 46 while(k<=m){ 47 if(a[k].r<i) 48 ma=max(ma,a[k].l),k++; 49 else break;} 50 l[i]=ma; 51 } 52 int hd=1,tl=0,j=0,ans=-1; 53 for(int i=1;i<=n;i++){ 54 if(l[i]>r[i]){f[i]=-inf;continue;} 55 while(j<=r[i]&&j<=n){ 56 while(hd<=tl&&f[j]>=f[que[tl]]) 57 tl--; 58 que[++tl]=j,j++;} 59 while(hd<=tl&&que[hd]<l[i]) 60 hd++; 61 f[i]=(hd<=tl)?(f[que[hd]]+1):1; 62 } 63 for(int i=l[n+1];i<=r[n+1];i++) 64 ans=max(ans,f[i]); 65 printf("%d\n",ans); 66 return 0; 67 }