code vs 2188 最长上升子序列(线段树优化DP)

2188 最长上升子序列

 时间限制: 1 s
 空间限制: 32000 KB
 题目等级 : 钻石 Diamond
题目描述 Description

LIS问题是最经典的动态规划基础问题之一。如果要求一个满足一定条件的最长上升子序列,你还能解决吗?

    给出一个长度为N整数序列,请求出它的包含第K个元素的最长上升子序列。

    例如:对于长度为6的序列<2,7,3,4,8,5>,它的最长上升子序列为<2,3,4,5>,但如果限制一定要包含第2个元素,那么满足此要求的最长上升子序列就只能是<2,7,8>了。

输入描述 Input Description

第一行为两个整数N,K,如上所述。

    接下来是N个整数,描述一个序列。

 

输出描述 Output Description

请输出两个整数,即包含第K个元素的最长上升子序列长度。

样例输入 Sample Input

8 6

65 158 170 299 300 155 207 389

样例输出 Sample Output

4

数据范围及提示 Data Size & Hint

80%的数据,满足0<n<=1000,0<k<=n

    100%的数据,满足0<n<=200000,0<k<=n


题解:线段树优化DP

把K之前小于K的值保留下来,K之后大于K的值保留下来,然后做最长上升子序列,就是这个题的答案,但是如果每次找当前点前面比这个点的值小的更新答案,时间复杂度n^2,所以我们可以把权值离散化,然后建立一颗权值线段树,来维护f[i]的值,每次更新的时候只需要求出这个点在权值线段树中之前点的最大值即可,0(n log n ) 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000003
using namespace std;
int n,k,a[N],f[N],num[N],cnt,mark;
int b[N],q[N],tr[N*8];
int cmp(int x,int y)
{
	return num[x]<num[y];
}
void update(int now)
{
	tr[now]=max(tr[now<<1],tr[now<<1|1]);
}
void pointchange(int now,int l,int r,int x,int v)
{
	if (l==r)
	{
		tr[now]=max(tr[now],v);
		return;
	}
	int mid=(l+r)/2; 
	if (x<=mid)  pointchange(now<<1,l,mid,x,v);
	else pointchange(now<<1|1,mid+1,r,x,v);
	update(now);
}
int qjmax(int now,int l,int r,int ll,int rr)
{
	if (l>=ll&&r<=rr)  return tr[now];
	int mid=(l+r)/2;
	int maxn=0;
	if (ll<=mid)  maxn=max(maxn,qjmax(now<<1,l,mid,ll,rr));
	if (rr>mid) maxn=max(maxn,qjmax(now<<1|1,mid+1,r,ll,rr));
	return maxn;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	 {
	 	if (a[i]<a[k]&&k>i)  num[++cnt]=a[i]; 
	 	if (a[i]>a[k]&&k<i)  num[++cnt]=a[i];
	 	if (k==i)  num[++cnt]=a[i],mark=cnt;
	 	b[cnt]=cnt;
	 }
	sort(b+1,b+cnt+1,cmp);
	int sz=0; num[b[0]]=1000000000;
	for (int i=1;i<=cnt;i++)  
	 if (num[b[i]]!=num[b[i-1]])  q[b[i]]=++sz;
	 else q[b[i]]=sz;
	f[1]=1;  pointchange(1,0,sz,q[1],f[1]);
	for (int i=2;i<=cnt;i++)
	 {
	 	if (q[i]!=1)  f[i]=qjmax(1,0,sz,0,q[i]-1)+1;
	 	else f[i]=1;
	 	pointchange(1,0,sz,q[i],f[i]);
	 }
	int ans=0;
	for (int i=mark;i<=cnt;i++) ans=max(ans,f[i]);
	printf("%d\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值