题目
Farmer John决定为他的N头排列好的奶牛(1 <= N<= 200,000)做一张全景合照。这N头奶牛分别以1..N进行编号。他一共拍了M(1<= M <=100,000)张相片,每张相片都只包含有一部分位置连续的奶牛:第i张照片涵盖着编号从a_i到b_i的所有奶牛。当然,这些照片合起来并不保证包含所有的牛。
Farmer John拍摄完所有的相片后,注意到一个很有趣的现象:他拍的每张照片中有且仅有一只奶牛身上有斑点。 FJ知道他的奶牛中有一部分是身上有斑点的,但他从来没有数过这种奶牛的数目。请根据FJ的这些照片,确定可能出现的斑点牛的最大的数目;若从FJ的照片中无法推测斑点牛的数目,则输出-1。
1 <= N<= 200,000
分析
这道题,要画画图理解才行。
其实就是要求
1.在每个区间里面都必须要有一个牛有斑点。
2.并且每个区间里面都必须只有一个牛有斑点
这两句话很关键。
于是我们可以根据这两个条件做dp。
首先我们设f[i]为前i个牛满足条件且最多能有多少牛有斑点,便有f[i]=max(f[j])+1
那么j的范围呢?
根据条件1,我们可以确定左边界
(因为必须选,那么上一个区间也一定要选,故j一定是>=最靠近i左边的区间的左坐标)
根据条件2,我们可以确定右边界。
(因为必须选一个,所以包含i的区间里面的任意一个j都不能转移,即j<=包含i的区间的最小的左坐标)
故我们可以根据这些限定用单调队列来优化原来的dp方程。
不得不说,这道题的思路确实很巧妙,竟能转换成dp,orz orz
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N=200005;
struct pop{
int x,y;
}a[N];
int n,m,st[N],en[N],f[N],q[N];
bool cmp(pop a,pop b){
return a.y<b.y;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+1+m,cmp);
int mat=0;
for(int i=1,j=1;i<=n;i++){
while (a[j].y<i&&j<=m) mat=max(mat,a[j].x),j++;
st[i]=mat;
}st[n+1]=st[n];
int mie=N+1;en[n+1]=n+1;
for(int i=n,j=m;i;i--){
while (a[j].y>=i&&j<=m) mie=min(mie,a[j].x),j--;
en[i]=mie;
}
int l=1;int r=0;
for(int i=1,j=0;i<=n+1;i++){
for(;j<en[i]&&j<i;j++){
if (f[j]<0) continue;
while (l<=r&&f[q[r]]<f[j]) r--;
q[++r]=j;
}
while (l<=r&&q[l]<st[i]) l++;
if (l<=r&&f[q[l]]!=-1) f[i]=f[q[l]]+(i!=n+1);else f[i]=-1;
}
printf("%d",f[n+1]);
}