BZOJ1986: [USACO2004 Dec] Dividing the Path 划区灌溉

L<=1000000的土地上用长度在2*A~2*B的线段覆盖所有点,且给定n<=1000个区间,每个区间上只允许有一条线段,求最少多少线段,无解-1。

f[i]表示填前i个土地最少线段,f(i)=f(j)+1,2*A<=i-j<=2*B,用个单调队列就行。注意区间是左闭右开。

至于那些坏区间,如果某个状态在覆盖了该点的区间的最远的右端点,那是合法的,否则一旦它被覆盖就是不合法状态。

为了处理这种情况,将区间按左端点排序,走到一个点时,若该点满足上面的情况,那就说明有一些区间的左端点<=i,把这些区间的右端点取个最大值来比较即可。出现这种情况后,把当前状态,以及当前状态到最远被覆盖区间右端点这些状态全部变成inf,然后i跳到那个右端点就可以做了。

不过,单调队列的节点添加不稳定,因此记个“单调队列加到哪里了”,这样就不怕跳来跳去的i了。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 #include<iostream>
 6 using namespace std;
 7  
 8 int n,L,A,B;
 9 #define maxn 1000011
10 struct Cow
11 {
12     int l,r;
13     bool operator < (const Cow &b) const {return l<b.l;}
14 }a[maxn];
15 int f[maxn],que[maxn],head,tail;
16 const int inf=0x3f3f3f3f;
17 int main()
18 {
19     scanf("%d%d%d%d",&n,&L,&A,&B);
20     for (int i=1;i<=n;i++)
21         scanf("%d%d",&a[i].l,&a[i].r),a[i].l++;
22     sort(a+1,a+1+n);
23     head=tail=0;
24     int last=0,k=1;
25     if (L&1) puts("-1");
26     else
27     {
28         for (int i=2;i<=L;i+=2)
29         {
30             while (head<tail && que[head]<i-2*B) head++;
31             for (int j=last;j<=i-2*A;j+=2)
32             {
33                 while (head<tail && f[que[tail-1]]>=f[j]) tail--;
34                 que[tail++]=j;
35             }
36             last=max(last,i-2*A+2);
37             int far=0;
38             while (k<=n && i>=a[k].l)
39             {
40                 far=max(far,a[k].r);
41                 k++;
42             }
43             if (i<far)
44             {
45                 int tmp=((far&1)?far+1:far)-2;
46                 for (;i<tmp;i+=2) f[i]=inf;f[i]=inf;
47                 last=max(last,i-2*B+2);
48                 continue;
49             }
50             if (head<tail) f[i]=f[que[head]]+1;
51             else f[i]=inf;
52         }
53         printf(f[L]>=inf?"-1\n":"%d\n",f[L]);
54     }
55     return 0;
56 }
View Code

 

posted @ 2017-09-22 14:30  Blue233333  阅读(173)  评论(0编辑  收藏  举报