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 }

 

posted @ 2018-11-30 20:51  guapisolo  阅读(198)  评论(0编辑  收藏  举报