以下面的题目为例(题目和代码在最后面),给定一个数列(长度最大为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)。
下面是原题和代码: