●BZOJ 3126 [Usaco2013 Open]Photo
题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=3126
题解:
单调队列优化DP,神奇。。
(好像某次考试考过,当时我用了差分约束+SPFA优化,然后过了。。。)
记 L[i] 表示i左边没有覆盖i点的区间中的最大的左端点
R[i] 表示覆盖i的区间中的最小的左端点的前一个位置,
那么,如果在i位置放一个点的话,在L[i]~R[i]里面也必须要放一个点。
(这两个数组可以O(N)计算前后缀最大最小值得到。)
即定义 DP[i] 为i位置放点时的总点数,
转移:DP[i]=max(DP[j])+1 (L[i]<=j<=R[i])
然后可以用单调队列优化。
和普通的单调队列有点不同,因为多了一个R[i]这个转移的右端点限制。
其实本质还是相同的~~
考虑到L[i],R[i]都单增,
所以在原来队列的首尾指针l,r的基础上多开一个rr指针就好了。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 200050 using namespace std; int L[MAXN],R[MAXN],F[MAXN]; int N,M; int main(){ static int Q[MAXN],l,r,_r; scanf("%d%d",&N,&M); for(int i=1;i<=N+1;i++) R[i]=i-1; for(int i=1,l,r;i<=M;i++){ scanf("%d%d",&l,&r); L[r+1]=max(L[r+1],l); R[r]=min(R[r],l-1); } for(int i=2;i<=N+1;i++) L[i]=max(L[i-1],L[i]); for(int i=N;i>=1;i--) R[i]=min(R[i],R[i+1]); l=_r=r=1; Q[1]=0; for(int i=1;i<=N+1;i++){ while(_r<=R[i]&&_r<=N){ if(F[_r]==-1){_r++; continue;} while(l<=r&&F[Q[r]]<=F[_r]) r--; Q[++r]=_r; _r++; } while(l<=r&&Q[l]<L[i]) l++; if(l<=r) F[i]=F[Q[l]]+(i!=N+1?1:0); else F[i]=-1; } printf("%d",F[N+1]); return 0; }
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas