●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;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8168933.html