题目大意:
求树上的最长上升子序列
解题思路:
- 可以用权值线段树合并来操作,每个点分别存储以该值结束的最长上升子序列以及以该值开始的最长上升子序列,最后合并即可
- 似乎还可以动态规划以及树上启发式合并来做
AC代码:
#include <bits/stdc++.h>
#define ft first
#define sd second
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e4;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, a[maxn], b[maxn];
vector <int> G[maxn];
struct SegTree {
int l, r;
int lis, lds; //sgt[u].lis以该点结束的最长上升子序列,sgt[u].lds以该点开始的最长上升子序列
} sgt[maxn * 30];
int rts[maxn], cnt, ans;
void update(int lc, int rc, int &rt, int p, int ulis, int ulds) {
if (!rt) rt = ++cnt;
sgt[rt].lis = max(ulis, sgt[rt].lis);
sgt[rt].lds = max(ulds, sgt[rt].lds);
if (lc == rc) return;
int mc = (lc + rc) >> 1;
if (p <= mc) update(lc, mc, sgt[rt].l, p, ulis, ulds);
else update(mc + 1, rc, sgt[rt].r, p, ulis, ulds);
}
int merge(int p, int q) { //q往p上合并
if (!p || !q) return p + q;
sgt[p].lis = max(sgt[p].lis, sgt[q].lis); //合并
sgt[p].lds = max(sgt[p].lds, sgt[q].lds);
ans = max(ans, sgt[sgt[p].l].lis + sgt[sgt[q].r].lds);
ans = max(ans, sgt[sgt[p].r].lds + sgt[sgt[q].l].lis);
sgt[p].l = merge(sgt[p].l, sgt[q].l);
sgt[p].r = merge(sgt[p].r, sgt[q].r);
return p;
}
pii query(int lc, int rc, int rt, int L, int R) {
if (!rt || lc > rc || R < lc || rc < L) return {0, 0};
if (L <= lc && rc <= R) return {sgt[rt].lis, sgt[rt].lds};
pii res = {0, 0}, res1, res2;
int mc = (lc + rc) >> 1;
res1 = query(lc, mc, sgt[rt].l, L, R);
res2 = query(mc + 1, rc, sgt[rt].r, L, R);
res = {max(res1.ft, res2.ft), max(res1.sd, res2.sd)};
return res;
}
void dfs(int u, int fu) {
int ulis = 0, ulds = 0;
for (auto v : G[u]) {
if (v == fu) continue;
dfs(v, u);
int lis = query(1, n, rts[v], 1, a[u] - 1).ft;
int lds = query(1, n, rts[v], a[u] + 1, n).sd;
ans = max(ans, lis + 1 + ulds);
ans = max(ans, ulis + 1 + lds);
rts[u] = merge(rts[u], rts[v]);
ulis = max(ulis, lis);
ulds = max(ulds, lds);
}
update(1, n, rts[u], a[u], ulis + 1, ulds + 1);
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
sort (b + 1, b + n + 1);
int cntb = unique(b + 1, b + n + 1) - (b + 1);
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + cntb + 1, a[i]) - b; //离散化
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
cout << ans << endl;
}

本文介绍了一种求解树上最长上升子序列的高效算法,通过权值线段树和动态规划相结合,展示了如何合并以节点为起点和终点的上升子序列,提供了一个C++实现的示例。理解并应用这些技术有助于解决类似问题。
2728

被折叠的 条评论
为什么被折叠?



