bzoj3126 [Usaco2013 Open]Photo

  省选前最后一天填坑......

  其实这个题早就想过,一直懒得写。我发现我写单调队列一次一个样,真是郁闷。

  简述题意就是给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点。

  我们考虑用f[i]表示前i个位置能放多少个,每一步决策有两个限制:1.每个区间里要有一个点,这样我们可以确定决策区间左边界;2.每个区间里只能有一个点,这样我们可以确定右边界。如果有地方左边界大于右边界了就是一定不能放点了。

  单调队列的话,首先让[ L[I],R[I] ]区间的f值入队,然后把队首不在这个决策区间里的出队,然后转移f[q[head]]就行了。

  总结起来,单队大概有两种,一种是先转移再入队,另一种是先入队再转移。前一种大多是针对斜率优化的,因为每个点对应一个斜率来T掉队里点的,也就是说每枚举到一个i就要更新一下单队,而每得到一个新的f值下一步立即就可以参与转移;而后一种大多是针对决策单调性的,因为每个点对应一个决策区间,这个区间是不断移动的,所以每得到一个f值是不能立即参与下一步转移的,而是要通过决策区间的移动来确定单队。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 #define maxn 220000
 7 #define inf 2147483647
 8 using namespace std;
 9 
10 struct seg
11 {
12     int x,y;
13 }a[maxn];
14 int l[maxn],r[maxn],q[maxn*2],f[maxn];
15 int n,m;
16 
17 bool cmp(seg a,seg b)
18 {
19     return a.y<b.y;
20 }
21 
22 int main()
23 {
24     //freopen("photo.in","r",stdin);
25     scanf("%d%d",&n,&m);
26     for (int i=1;i<=m;i++) 
27         scanf("%d%d",&a[i].x,&a[i].y);
28     sort(a+1,a+m+1,cmp);
29     int mx=0;
30     for (int i=1,j=1;i<=n;i++)
31     {
32         while (a[j].y<i&&j<=m) mx=max(mx,a[j].x),j++;
33         l[i]=mx;
34     }
35     l[n+1]=1;
36     int mi=n;
37     for (int i=n,j=m;i;i--)
38     {
39         while (a[j].y>=i&&j) mi=min(mi,a[j].x-1),j--;
40         r[i]=mi;
41     }
42     r[n+1]=n;
43     //for (int i=1;i<=n;i++) cout<<l[i]<<' '<<r[i]<<endl;
44     int head=1,tail=0;
45     for (int i=1,j=0;i<=n+1;i++)
46     {
47         for (;j<=r[i]&&j<i;j++)
48         {
49             if (f[j]==-1) continue;
50             while (head<=tail&&f[j]>f[q[tail]]) tail--;
51             q[++tail]=j;
52         }
53         while (head<=tail&&q[head]<l[i]) head++;
54         if (head<=tail) f[i]=f[q[head]]+(i!=n+1); else f[i]=-1;
55     }
56     printf("%d\n",f[n+1]);
57     return 0;
58 }
photo

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/17/3083072.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值