1. 程式人生 > >●BZOJ 3126 [Usaco2013 Open]Photo

●BZOJ 3126 [Usaco2013 Open]Photo

cst problem 。。 差分 最大 nbsp min string target

題鏈:

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

  

●BZOJ 3126 [Usaco2013 Open]Photo