P1020 [NOIP1999 普及组] 导弹拦截

nlogn解决最长上升/不下降/下降/不上升子序列问题。

设f[len]表示子序列长度为len时序列最后一个数是什么。

那么可以根据具体情况来讨论,一种是直接++len,将其存在数列最后,另一种是用更优的更新不优的。

此题中第一问不难看出是求一个最长不下降子序列,而第二问,相当于求一个最长上升子序列:

我们假定对于a[i]序列已经分成了k组,那么在这k组中,一定会在k+1组中找到一个数比k组中的某一个(几个)数大(否则可将k与k+1组合并),那么相当于k个组为一个单调递增的序列,求这个增序列的最大长度即为答案。

自己总结如下:

严格单调上升或下降子序列,找的时候用lower_bound,反之用upper_bound。(用cmp可以从找第一个大于/大于等于的数转为找第一个小于/小于等于的数)

严格单调上升或下降子序列,用>或<,反之用>=或<=。

(在下面程序中进行修改)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005;
typedef long long ll;
int cnt,tot,n;
int a[N],f[N],d[N];
bool cmp(int a,int b){
	return a>b;
}
int main(){
	while(cin>>a[++n]);
	n--;
	memset(f,0x3f3f,sizeof(f));
	for(int i=1;i<=n;i++){
		if(a[i]<=f[cnt]) f[++cnt]=a[i];//不上升 
		else f[upper_bound(f+1,f+cnt+1,a[i],cmp)-f]=a[i];
		if(a[i]>d[tot]) d[++tot]=a[i];
		else d[lower_bound(d+1,d+tot+1,a[i])-d]=a[i];
	}
	printf("%d
%d",cnt,tot);
	return 0;
}
//lower_bound会找出序列中第一个大于等于x的数

//upper_bound会找出序列中第一个大于x的数
原文地址:https://www.cnblogs.com/New-ljx/p/15350563.html