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 }