线段树求LIS并统计最长子序列个数

博客介绍了如何利用线段树解决求解数列中最长先增后减子序列长度及个数的问题。首先,通过线段树计算每个位置的最长单增和单减子序列长度,接着遍历数据统计最长先增后减子序列的个数,优化后的算法将时间复杂度降低到O(n)。
摘要由CSDN通过智能技术生成

以下面的题目为例(题目和代码在最后面),给定一个数列(长度最大为10000),求出最长的先增后减子序列长度及个数。做法是先求出以每一个位置结尾的最长单增子序列长度以及以该位置开头的最长单减子序列长度,然后遍历所有位置找出最大先增后减子序列长度。

以最长单增序列(LIS)为例,由于不仅需要整个序列LIS的长度,还要保存以每个位置为结尾位置的LIS长度。记以a[i]结尾的LIS长度为dp[i],则

dp[i] = max{dp[j] | a[j] < a[i]} + 1

这就是一个RMQ问题,涉及单点更新和单点查询,所有选择用线段树求解。

后面统计子序列个数时,记以a[i]结尾的最长子序列个数为num[i],则

num[i] = sum{num[j] | j < i && dp[j] + 1 == dp[i]}

最朴素的做法是遍历1到i - 1,但这样会超时,由于dp[i] <= 10000,故提前保存每个dp[i]的位置,用coll[i]保存所有dp[j] = i的j,那么对于num[i],只需遍历集合coll[dp[j] - 1],统计子序列个数的时间复杂度可以降为O(n)。

下面是原题和代码:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值