题目描述 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);
}