【模板】最长公共子序列 详细解析
原题:link,点击这里喵。
朴素 dp:
首先考虑朴素的 dp,记 wxw_xwx 为 xxx 在序列 AAA 中的位置,显然我们有以下转移方程:
dpi=maxj<i∧wBi>wBjdpj+1dp_i=\max_{j<i \land w_{B_i}>w_{B_j}}dp_j+1 dpi=j<i∧wBi>wBjmaxdpj+1
解释:如果我们要从之前的序列转移过来,那么一定要满足当前元素的下标在 AAA 与 BBB 中都要分别在被转移的元素后面,时间复杂度为 O(n2)O(n^2)O(n2)。
优化 dp:
观察朴素 dp,我们要求的便是在当前元素下标之前的最大 dp 值,即之前的元素在 AAA 中下标比当前元素小,因为不用差分,考虑使用树状数组维护比某下标小的 dp 最大值,此时查询满足条件的时间复杂度变为 O(logn)O(\log n)O(logn),总时间复杂度优化为 O(nlogn)O(n\log n)O(nlogn)。
树状数组写法:
struct traymax{#define lowbit(x) ((x)&(-x))int g[N];void update(int b,int k){while(b<N){g[b]=max(g[b],k);b+=lowbit(b);}}int query(int b){int temp=0;while(b){temp=max(temp,g[b]);b^=lowbit(b);}return temp;}
}t;
code:
#include <bits/stdc++.h>#define inf 0x3f3f3f3f
typedef long long lnt;using namespace std;const int N = 2e5;#define lowbit(x) ((x) & (-x))struct traymax {int g[N];void update(int b, int k) {while (b < N) {g[b] = max(g[b], k);b += lowbit(b);}}int query(int b) {int temp = 0;while (b) {temp = max(temp, g[b]);b ^= lowbit(b);}return temp;}
} t;int w[N], date[N], dp[N], n, ans;int main() {scanf("%d", &n);for (int i = 1; i <= n; ++i) {scanf("%d", &w[i]);}for (int i = 1; i <= n; ++i) {scanf("%d", &date[i]);dp[i] = t.query(w[date[i]]) + 1;t.update(w[date[i]], dp[i]);ans = max(ans, dp[i]);}printf("%d", ans);return 0;
}