P1020 [NOIP1999 普及组] 导弹拦截

236 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

[NOIP1999 普及组] 导弹拦截

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

一行,若干个整数,中间由空格隔开。

输出格式

两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

样例 #1

样例输入 #1

389 207 155 300 299 170 158 65

样例输出 #1

6
2

提示

对于前 50%50\% 数据(NOIP 原题数据),满足导弹的个数不超过 20002000 个。该部分数据总分共 100100 分。可使用O(n2)\mathcal O(n^2) 做法通过。
对于后 50%50\% 的数据,满足导弹的个数不超过 10510^5 个。该部分数据总分也为 100100 分。请使用 O(nlogn)\mathcal O(n\log n) 做法通过。

对于全部数据,满足导弹的高度为正整数,且不超过 5×1045\times 10^4

此外本题开启 spj,每点两问,按问给分。


upd 2022.8.24\text{upd 2022.8.24}:新增加一组 Hack 数据。

分析

这表面上看起来是一个很简单的最长上升子序列,但是如果仅仅是最长上升子序列模板是一定会超时的,因此我们要思考怎么优化,由于我们只要考虑输出拦截的导弹数量,所以我们可以用二分答案。

代码

#include <iostream>
using namespace std;
const int N=100010;
int dp1[N],dp2[N],a[N];
int main(){
	int n=1;
	while(cin>>a[n]) n++;
	n--;
	int cnt=1;
	dp1[1]=a[1];
	for(int i=2;i<=n;i++){
		if(a[i]<=dp1[cnt]){
			cnt++;
			dp1[cnt]=a[i];
		}
		else{
			int l=1,r=cnt;
			while(l<r){
				int mid=l+r>>1;
				if(dp1[mid]<a[i]) r=mid;
				else l=mid+1;
			}
			dp1[r]=a[i]; 
		}
	}
	dp2[1]=a[1];
	int res=1;
	for(int i=2;i<=n;i++){
		if(a[i]>dp2[res]){
			res++;
			dp2[res]=a[i];
		}
		else{
			int l=1,r=res;
			while(l<r){
				int mid=l+r>>1;
				if(dp2[mid]>=a[i]) r=mid;
				else l=mid+1;
			}
			dp2[r]=a[i];
		}
	}
	cout<<cnt<<endl;
	cout<<res<<endl;
	return 0;
}

希望能帮助到大家(QAQQAQ)!