The Robinson–Schensted–Knuth (RSK) correspondence maps a sequence to a pair of tableaux of the same shape: (insertion tableau) has weakly increasing rows, (recording tableau) stores when each cell appeared. The shape answers subsequence questions: its first row is the LIS, its number of rows is the longest strictly decreasing subsequence, and by Greene’s theorem the first rows together give the largest union of disjoint increasing subsequences.

QuantityFrom the shape
longest increasing subsequence
longest decreasing subsequence (distinct values)number of rows
max total size of disjoint increasing subsequences (Greene)
max total size of disjoint decreasing subsequencessum of the first column lengths
number of permutations with shape , where = number of standard tableaux (hook length formula)
auto [P, Q] = rsk(a);                    // a: any values; rows of P weakly increasing
vector<int> shape = rskShape(a);         // row lengths; shape[0] = LIS length
vector<vector<int>> rows = fastRsk(a);   // insertion tableau P in O(n sqrt n log n) (distinct values)

Row insertion: insert into row 0; if some element is greater than , the smallest such element is bumped to the next row (use for strictly increasing rows); otherwise is appended. records the step at each new cell. Complexity: rsk is , which is for random permutations but in the worst case (a decreasing sequence). fastRsk computes the first rows directly and the rest from the first rows of the reversed sequence (whose tableau is the transpose for distinct values), for always (Benq / msg555).

template <class T>
pair<vector<vector<T>>, vector<vector<int>>> rsk(const vector<T>& a) {
    vector<vector<T>> P;
    vector<vector<int>> Q;
    for (int step = 0; step < (int)a.size(); step++) {
        T x = a[step];
        for (int r = 0;; r++) {
            if (r == (int)P.size()) {
                P.push_back({x});
                Q.push_back({step});
                break;
            }
            auto it = upper_bound(P[r].begin(), P[r].end(), x);
            if (it == P[r].end()) {
                P[r].push_back(x);
                Q[r].push_back(step);
                break;
            }
            swap(x, *it);
        }
    }
    return {P, Q};
}
 
template <class T>
vector<int> rskShape(const vector<T>& a) {
    auto P = rsk(a).first;
    vector<int> shape;
    for (auto& row : P) shape.push_back(row.size());
    return shape;
}
 
vector<vector<int>> boundedRsk(const vector<int>& a, int k) {   // first k rows of the insertion tableau
    vector<vector<int>> h(k);
    for (int x : a) {
        for (int j = 0; j < k; j++) {
            int p = lower_bound(h[j].begin(), h[j].end(), x) - h[j].begin();
            if (p == (int)h[j].size()) {
                h[j].push_back(x);
                break;
            }
            swap(x, h[j][p]);
        }
    }
    return h;
}
 
vector<vector<int>> fastRsk(vector<int> a) {   // distinct values
    if (a.empty()) return {};
    int rt = (int)ceil(sqrt((double)a.size()));
    auto ha = boundedRsk(a, rt);
    reverse(a.begin(), a.end());
    auto hb = boundedRsk(a, rt);   // rows of the reversed sequence = columns of the original tableau shape
    ha.resize(hb[0].size());
    for (int i = rt; i < (int)hb[0].size(); i++) {
        for (int j = 0; i < (int)hb[j].size(); j++) ha[i].push_back(hb[j][i]);
    }
    while (!ha.empty() && ha.back().empty()) ha.pop_back();
    return ha;
}

Related: Sequence DP (LIS), Counting Fundamentals.