490F - Treeland Tour 树上最长上升子序列

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

题目大意:

求树上的最长上升子序列

解题思路:

  • 可以用权值线段树合并来操作,每个点分别存储以该值结束的最长上升子序列以及以该值开始的最长上升子序列,最后合并即可
  • 似乎还可以动态规划以及树上启发式合并来做

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值