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 }