●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

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号