照片 usaco 2013 open photo

题目

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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值