【题解】LIS(longest increasing subsequence)最长上升子序列
求最长上升子序列
太简单了不想写。。。。。
求其最长长度的方案数
第一种,复杂度为\(n^2\)的方法
再开一个和f[][]数组长得差不多的g[][]数组,f[][]是放长度的,g[][]是放方案数的
先看看代码
for(int i=1;i<=n;i++){
f[i]=1;g[i]=1;
for(int j=1;j<i;j++){
if(a[j] < a[i]){
int l=f[j]+1;
if(l == f[i]) g[i] += g[j];
if(l > f[i]){
g[i]=g[j];
f[i]=l;
}
}
}
}
我们从第j个数转移到第i个数,
如果\(f_{[j]} + 1\) 要大于\(f_{[i]}\),那他很优秀地把\(f_{[i]}\)更新成了\(f_{[j]} + 1\),把\(g_{[i]}\)更新成了\(g_{[j]} + 1\);
如果\(f_{[j]} + 1\) 和 \(f_{f[i]}\)相等,那么\(f_{[i]}\)还是\(f{[i]}\),不过方案数就要增加了,\(g_{[i]}\)要加上\(g_{[j]}\);
如果f[j]+1要小于f[i],那就不管他(很显然这里只有两个if,没他的份)
然后以上两个if还可以这样写:
if(l > f[i]){
f[i]=l;g[i]=0;
}
if(f[i]==l){
g[i]+=g[j];
}
对了,还有输出其中一种方案的话
可以加一个pre[]数组,记录当前最长序列的上一个数是几,也就是更新\(f_{[i]}\)时,记下来\(pre_{[i]} = j\)
输出时,我们要找的就是那个\(f_{[i]}\)等于答案的i,输出\(a_{[i]}\)然后顺藤摸瓜追根溯源输出\(a_{pre_{[i]}}\),然后是\(a_{pre_{[pre_{[i]}]}}\)...
第二种,更快的方法复杂度为\(nlog^n\)(其实是如果序列里的最大值和n一个级别的话才是nlogn,本来是\(O{nlogm(m=max(a_{[1]},a_{[2]},...))}\)
前方高能,注意,是线段树!
对于\(a_{[i]}\),我们要找的是\(a_{[j]}\)比他小,并且\(f_{[j]}\)最长的这个\(a_{[j]}\)
我们要求区间最值了
搞一个线段树,搞一个\(m=max{a1,a2,a3...an}\),m是所有数的最大值,线段树区间下标是1~m,也就是以a[]做下标
线段树的区间里存的是最长上升序列的长度,即leaf[a[i]]=fi
然后我们一步步的循环1~n,对于a[i],我们要找到比他小的a[j],并且是要f[j]最大的。
我们单点查询1~a[i]-1(这些都满足a[j]<a[i]虽然不一定有这么一个a[j])运用线段树,在logm的时间里搞出一个最大的\(f_{[j]}\),拿他加1作为\(f_{[i]}\)。
又臭又长的线段树虽然别的不会但是单点查询还是能写的呜呜