This problem extends the classic Longest Increasing Subsequence (LIS) problem. Instead of finding the length of the LIS, we count how many subsequences achieve that maximum length.
A subsequence is formed by deleting zero or more elements from the array without changing the order of the remaining elements. "Strictly increasing" means each element must be larger than the previous one (not equal).
The complication is that we have to track two quantities at every position, not one: the length of the longest increasing subsequence ending there, and the number of distinct subsequences that reach that length. Finding the maximum length alone is the standard LIS problem. Counting requires propagating the second quantity correctly as longer subsequences are built.
1 <= nums.length <= 2000 --> With n up to 2000, an O(n^2) solution runs about 4 million operations and passes comfortably. O(n^3) would be 8 billion, which is too slow.-10^6 <= nums[i] <= 10^6 --> Values can be negative and span a wide range, so a value-indexed array is not practical. We compare values directly, and if we want a value-indexed structure we coordinate-compress first.Generate every increasing subsequence, find the maximum length among them, and count how many reach that maximum. Recursion (backtracking) enumerates all of them: at each index, either include the current element (when it is greater than the last included element) or skip it.
This is correct but generates an exponential number of subsequences, so it only works for small inputs. The counting logic in the faster approaches follows the same two branches seen here: a longer subsequence resets the best length, an equal-length one adds to the count.
Trace nums = [1, 3, 5, 4, 7]. The recursion starts with lastVal = -infinity and currentLen = 0, then branches on each index.
Each distinct increasing subsequence corresponds to exactly one path through the recursion, because every path picks a fixed set of indices in order. The two paths that reach length 4 are:
maxLen becomes 4 and count becomes 1.currentLen == maxLen and count increments to 2.Every other path produces a subsequence of length 3 or less (for example 1, 3, 5 or 3, 4, 7), so none of them affect count once maxLen is 4. After the full recursion, count is 2, which is the answer.
The exponential cost comes from recomputing the same suffixes repeatedly. The next approach computes the LIS length and count ending at each index once and reuses earlier results.
The standard LIS DP approach computes, for each index i, the length of the longest increasing subsequence ending at i. We do this by looking at every earlier index j where nums[j] < nums[i] and taking the maximum length[j] + 1.
To count the number of LIS, we add a second array cnt alongside the length array. cnt[i] tracks how many increasing subsequences of length length[i] end at index i. When we examine a pair (j, i) where nums[j] < nums[i], there are three cases:
length[j] + 1 > length[i]: we found a longer subsequence ending at i. Update length[i] and reset cnt[i] = cnt[j].length[j] + 1 == length[i]: we found another set of subsequences with the same max length ending at i. Add cnt[j] to cnt[i].length[j] + 1 < length[i]: this path is shorter than what we already have. Ignore it.After filling both arrays, find the maximum value in length, then sum up cnt[i] for every index where length[i] equals that maximum.
Setting cnt[i] = cnt[j] means every length-length[j] subsequence ending at j extends to a longer one ending at i by appending nums[i]. Adding cnt[j] to cnt[i] records additional subsequences that reach the same length through a different predecessor j. No subsequence is double-counted, because each one has a unique predecessor j (the index of its second-to-last element).
The argument relies on processing indices left to right. When index i is computed, every predecessor j < i already holds its final length[j] and cnt[j], so the values read for i are complete.
length[i] = 1 and cnt[i] = 1 for all i (each element by itself is a subsequence of length 1, and there is exactly one such subsequence).i from 1 to n-1, scan all previous indices j from 0 to i-1.nums[j] < nums[i]:length[j] + 1 > length[i]: set length[i] = length[j] + 1 and cnt[i] = cnt[j].length[j] + 1 == length[i]: add cnt[j] to cnt[i].maxLen as you go.cnt[i] for all indices where length[i] == maxLen. Return that sum.length and cnt), plus a few variables.This is fast enough for n <= 2000. For larger inputs, the O(n) inner scan of predecessors becomes the bottleneck. The next approach replaces that scan with an O(log n) segment tree query.
The O(n^2) approach spends O(n) time per element querying all predecessors. We can speed this up by framing the query differently: for each nums[i], we want the maximum LIS length (and the count of subsequences achieving that length) among all elements with value strictly less than nums[i].
This is a range-max query, which a segment tree handles in O(log n). We coordinate-compress the values, build a segment tree over the compressed values, and for each element, query then update.
Each node in the segment tree stores a pair (maxLength, count): the longest LIS length among subsequences ending with a value in that range, and the total number of such subsequences.
Processing left to right keeps the same invariant as the DP: when nums[i] is processed, the tree holds the best (length, count) for every value already seen. Querying the range of ranks [1, rank(nums[i]) - 1] returns the best subsequence among all strictly smaller values, which is exactly the set of valid predecessors.
The merge defines how two range results combine: keep the longer length, and when two ranges tie on length, sum their counts. This matches the two branches of the DP (a longer predecessor replaces, an equal-length one accumulates), so the count stays correct while the inner scan drops from O(n) to O(log n).
nums to map them to the range [1, m] where m is the number of distinct values.(maxLength, count), initialized to (0, 0).nums[i] (left to right):(prevLen, prevCnt) among all smaller values.newLen = prevLen + 1. If prevLen == 0, set newCnt = 1. Otherwise, newCnt = prevCnt.compressed(nums[i]) by merging (newLen, newCnt) with whatever is already stored there.For nums = [1, 3, 5, 4, 7] with compressed ranks {1:1, 3:2, 4:3, 5:4, 7:5}: