1 条题解

  • 0
    @ 2025-8-24 22:01:35

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar dodo
    **

    搬运于2025-08-24 22:01:35,当前版本为作者最后更新于2019-03-28 15:23:04,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    楼上两篇题解写的有一点点复杂,有map还写了离散化……

    差分固然是一种理解方式,但其实有一种更好的理解方法和更简洁的代码。

    那么现在我就来讲一讲

    题意简述

    文字语言:求树上最大权值随祖孙关系不降的点集大小

    数学语言:求 Smax|S_{max}| 使得 i,j(ancestor of i)S,wiwj\forall{i,j(ancestor\ of \ i)\in S}, w_i\leq w_j

    为了方便描述,我们定义这种集合为“树上LIS”。

    题解

    考虑采用数学归纳法

    类似处理序列LIS问题,对于每一个点 uu 使用multiset维护一个集合 fuf_u 满足以下性质

    • fu,if_{u,i} 表示在 uu 的子树中选择 ii 个点组成的所有树上LIS中,级别值 ww 最小值最大的那一个。

    • uu 为根节点的 ansu=fuans_u=|f_u|fu|f_u|表示集合 fuf_u 的大小)(该性质可由上述性质发现)

    对于任意一个叶子节点 uu, fuf_u显然只含有 wuw_u,满足树上LIS性质。

    再考虑不是叶子节点的 uu

    假设点 uu 的所有孩子 vvfvf_v 已经满足求出并满足上述性质,我们应该如何求出 uufuf_u 呢?

    首先,显然 uu 的所有孩子不会相互影响,要从以 uu 为根节点的子树(除 uu )中选出大小为 ii 的树上LIS,可以直接贪心地选所有孩子集合中最大的 ii 个,于是只需将全部 fvf_v 取并集并排序即可,于是可以直接将孩子们的 fvf_v 集合全部启发式合并丢入 fuf_u 的multiset ,记 S=vu.sonfvS=\bigcup_{v\in u.son}f_v

    现在我们考虑将 uu 加入 SS 集合并使集合满足性质

    我们直接在multiset上二分出第一个 ii 满足 fu,iwuf_{u,i}\geq w_u 那么我们将 uu 接在 ii 前显然是最优方案,此时 fu,i1f_{u,i-1} 就可以被 wuw_u 替换,那么现在的集合就是我们要求的 fuf_u,并且满足树上LIS性质。

    按照这样的方式在树上dfs即可求出 f1f_1,此时答案即为 f1|f_1|

    复杂度证明

    该算法的复杂度为 O(nlog2n)O(nlog^2n)

    考虑同样采用数学归纳法

    TuT_u 表示处理出 fuf_u 的时间复杂度,SuS_u表示 uu 的子树大小

    我们需要证明 Tu=Sulog2SuT_u=S_ulog^2S_u

    对于任意一个叶子节点 uuSu=1S_u=1,此时只需在multiset中插入 wuw_u 复杂度为 O(1)O(1),满足Ti=Sulog2SuT_i=S_ulog^2S_u

    再考虑不是叶子节点的 uu

    假设点 uu 的所有孩子 vvTv=Svlog2SvT_v=S_vlog^2S_v

    那么 Ti=vu.sonTv+TmergeT_i=\sum_{v\in u.son}T_v+T_{merge}

    因为子孙们包含的节点个数vu.sonSv+1=Su\sum_{v\in u.son}S_v+1=S_u

    所以vu.sonTvSulog2Su\sum_{v\in u.son}T_v\leq S_ulog^2S_u

    启发式合并的复杂度为 SulogSuS_ulogS_u,使用multiset维护加一个log,Tmerge=Sulog2SuT_{merge}=S_ulog^2S_u

    所以TuT_uSulog2SuS_ulog^2S_u 同阶

    证毕。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5 + 5;
    multiset<int> f[N];
    multiset<int>::iterator it;
    int n, w[N], ans;
    int h[N], to[N], nxt[N], t;
    bool comp(int x, int y) { return w[x] < w[y]; }
    void add(int u, int v) { to[++t] = v, nxt[t] = h[u], h[u] = t; }
    void merge(int u, int v) {
        if(f[u].size() < f[v].size()) swap(f[u], f[v]);
        for(it = f[v].begin(); it != f[v].end(); ++it) f[u].insert(*it);
    }
    void dfs(int u) {
        for(int i = h[u]; i; i = nxt[i]) dfs(to[i]), merge(u, to[i]);
        f[u].insert(w[u]);
        it = f[u].lower_bound(w[u]);
        if(it != f[u].begin()) f[u].erase(--it);
    }
    int main() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
        for(int i = 2; i <= n; ++i) {
            int f;
            scanf("%d", &f);
            add(f, i);
        }
        dfs(1);
        printf("%d", f[1].size());
    }
    
    • 1

    信息

    ID
    3562
    时间
    1000ms
    内存
    250MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者