MuXinCG's Blog

Back

高维前缀和#

高维前缀和的拓展-子集和DP(SOSDP)#

位运算求最长公共子序列#

最长不下降子序列O(n log n)写法#

先来描述一个题目,题目很常见

给定一个长度为nn的序列aa,求出一个最长的aa的子序列,满足该子序列的最后一个元素不小于前一个元素

考虑状态(i,l)(i, l),表示序列以地ii个元素结尾的不下降子序列最长为ll,不同于以往按固定ii处理状态的方法,这里直接判断(i,l)(i, l)是否合法

  • 初始状态(1,1)(1, 1)必然合法
  • 对于任意(i,l)(i, l),如果存在j<ij < i(j,l1)(j, l - 1)合法,,同时ajaia_j \le a_i,则(i,l)(i , l )合法

最终,只需要找到合法状态中ll最大的(i,l)(i, l),即可得到最长不下降子序列的常务

设原序列为a1,,ana_1,\cdots,a_n,定义数组dd,其中第xx位表示

  • 如果aia_i大于等于序列dd
    • aia_i大于等于当前最长子序列的末尾元素,说明存在一个不下降子序列可以接上aia_i,不插入将破坏最优性
  • 如果aia_i严格小于dd的最后一个元素,找到第一个大于它的元素,并用aia_i替换它
    • 若直接插在末尾,会破坏dd的单调性,替换操作可以保证每个长度的末尾元素尽可能小,从而为后续元素保留更多可能性

以下是代码实现

int n, a[MAXN], d[MAXN], di[MAXN], pre[MAXN], res[mAXN];

int dp() {
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int tmp = std::upper_bound(d, d + ans, a[i]) - d;
        pre[i] = tmp ? di[tmp - 1] : -1;
        d[tmp] = a[i]l
        di[tmp] = i;
        if (tmp == ans) ++ans;
    }
    for (int k = ans, i = di[ans - 1]; k; --k) {
        res[k] = a[i];
        i = pre[i];
    }
    return ans;
}
cpp

DP优化#

Algorithm Miscellany - Dynamic Programming
https://muxincg2004.github.io/blog/algo-dp
Author Ziheng Zhang
Published at March 10, 2026