[USACO13OPEN] Photo G

洛谷[USACO13OPEN] Photo G

题目大意

n n n头奶牛,编号为 1 1 1 n n n。有 m m m个区间 [ l , r ] [l,r] [l,r],每个区间 [ l , r ] [l,r] [l,r]都有且只有一头有斑点的奶牛。求这些牛中最多有多少头有斑点的奶牛。如果出现矛盾,则输出 − 1 -1 1

1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 1 0 5 1\leq n\leq 2\times 10^5,1\leq m\leq 10^5 1n2×105,1m105


题解

f i f_i fi表示第 i i i头奶牛是有斑点的奶牛时前 i i i头奶牛中最多有多少头有斑点的奶牛,那么转移式为

f i = max ⁡ ( f j ) + 1 f_i=\max(f_j)+1 fi=max(fj)+1

其中 j < i j<i j<i j j j满足一定条件。

对于 j j j转移到 i i i,即 j j j i i i都是有斑点的奶牛, [ j + 1 , i − 1 ] [j+1,i-1] [j+1,i1]上都是没有斑点的奶牛。

依题意,每个区间 [ l , r ] [l,r] [l,r]都有且只有一头有斑点的奶牛,我们可以把这个条件转化为每个区间最多有一条奶牛,最少有一条奶牛。

最多有一头奶牛

如果 i i i j j j在同一个区间 [ l , r ] [l,r] [l,r]中,则这个区间会有两头奶牛。那么,对于任意包含 i i i的区间 [ l , r ] [l,r] [l,r] j j j需要满足小于 l l l

m n i mn_i mni表示包含 i i i的区间中最小的 l l l,则

m n i = min ⁡ ( 右端点为 i 的区间的最小的 l , m n i + 1 ) mn_i=\min(右端点为i的区间的最小的l,mn_{i+1}) mni=min(右端点为i的区间的最小的l,mni+1)

即在右端点等于 i i i和右端点大于 i i i的所有区间中求出最小的左端点。

最少有一头奶牛

如果区间 [ l , r ] [l,r] [l,r] i i i j j j之间,则这个区间没有奶牛。那么,对于任意 r < i r<i r<i的区间, j j j需要满足大于等于 l l l

m x i mx_i mxi表示右端点小于 i i i的区间中最大的 l l l,则

m x i = max ⁡ ( 右端点为 i − 1 的区间的最大的 l , m x i − 1 ) mx_i=\max(右端点为i-1的区间的最大的l,mx_{i-1}) mxi=max(右端点为i1的区间的最大的l,mxi1)

即在右端点等于 i − 1 i-1 i1或右端点小于 i − 1 i-1 i1的所有区间中求出最大的左端点。

转移式

那么,我们就能得到 f i f_i fi的转移式:

f i = f j + 1 ( m x i ≤ j < m n j ) f_i=f_j+1\qquad(mx_i\leq j< mn_j) fi=fj+1(mxij<mnj)

我们可以在最后设一个点 n + 1 n+1 n+1来统计答案。

因为 m x j mx_j mxj m n j mn_j mnj都单调不下降,所以我们可以用单调队列来优化。

时间复杂度为 O ( n ) O(n) O(n)

code

#include<bits/stdc++.h>
using namespace std;
int n,m,hd=1,tl=0,mn[200005],mx[200005],q[200005],f[200005];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n+1;i++) mn[i]=i;
	for(int i=1,l,r;i<=m;i++){
		scanf("%d%d",&l,&r);
		mn[r]=min(mn[r],l);
		mx[r+1]=max(mx[r+1],l);
	}
	for(int i=n;i>=1;i--){
		mn[i]=min(mn[i],mn[i+1]);
	}
	for(int i=1;i<=n+1;i++){
		mx[i]=max(mx[i],mx[i-1]);
	}
	q[++tl]=0;
	for(int i=1,j=1;i<=n+1;i++){
		for(;j<mn[i];j++){
			if(f[j]==-1) continue;
			while(hd<=tl&&f[q[tl]]<f[j]) --tl;
			q[++tl]=j; 
		}
		while(hd<=tl&&q[hd]<mx[i]) ++hd;
		if(hd<=tl) f[i]=f[q[hd]]+(i!=n+1);
		else f[i]=-1;
	}
	printf("%d",f[n+1]);
	return 0;
}
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值