NOIP1999拦截导弹(dp+二分 nlogn)

本文解析了一道涉及数据范围达1e5的题目,介绍了如何利用dp+二分和线段树解决最长上升子序列长度问题,并结合Dilworth定理探讨了最长下降子序列长度的应用。通过实例讲解了lower_bound和upper_bound在求解中的关键作用。
摘要由CSDN通过智能技术生成

题目:

 注意:数据范围高达1e5,所以用O(n^2)的时间复杂度肯定不行,也就是普通的两重循环的dp不行了……所以从这题学到(只是看懂)dp+二分的解法,还有用线段树求最长上升子序列长度的,后面这两种都是O(nlogn)的复杂度,(雪长说线段树的“更容易理解”:“相当于把dp 的第二层循环变为log的”)【但问题是线段树我忘光了呀qwq】

转化:此题第一问即(我)常见的 求最长不上升子序列长度(只是数据限制的方法我个菜鸡不常见……)重点是第二问,由Dilworth定理得:可划分的最少不上升子序列的数目就是其最长下降子序列的长度

 然后自己对着样例走一遍就好,上图可谓难得的讲得“通俗易懂”的惹QAQ

注意lower_bound(s,s+n,a[i])是在s的[ 0,)中找到第一个“大于等于”a[i]的位置,那么对于s此时为50(s[0]),170(s[1]),而在检索a数组时又碰到一个170,那么(lower_bound)所找到的d==1,s[1]再次被赋予了170那么最后得到的s就没有重复的元素,所以它是“上升”的,不是“不下降”

那么同样的道理,upper_bound找的是第一个“大于”的位置,就不是覆盖掉相等的元素,也即可重复存在,是最长“不下降”子序列用的。【当然你把lower_bound里的a[i]加个1与upper_bound  a[i]作用相同哈】

#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e5+6,inf=0x3f3f3f3f;
int n,a[M],ct,len,s[M];//Dilworth定理 
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	while(cin>>a[n]){n++;}
	fill(s,s+n,inf);
	for(int i=0;i<n;i++){           //求最长"上升"子序列长度
		int d=lower_bound(s,s+n,a[i])-s;  //找第一个大于等于该数的位置 
		s[d]=a[i];
		ct=max(ct,d);
	}
	fill(s,s+n,inf);
	reverse(a,a+n);   //倒过来  再求最长"不下降"子序列长度 
	for(int i=0;i<n;i++){
		int d=upper_bound(s,s+n,a[i])-s;
		s[d]=a[i];
		len=max(len,d);
	}
	cout<<len+1<<'\n'<<ct+1;//因下标从0开始,故长度为下标+1 
	return 0;
}

线段树不会,先就这样了叭这题(润

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值