洛谷 P5290 [十二省联考 2019] 春节十二响 解题报告

洛谷 P5290 [十二省联考 2019] 春节十二响 解题报告

link

题意

给定一些树上父子关系及各点权值,该树根为1号点。

将所有点划分成一些互不相交的集合,“集合”定义为__内部的点不能具有父子关系的集合__,“集合的价值”定义为__集合中的所有点的最大值__。

求所有集合的价值之和最小。

Solutions

如图为一棵以a为根节点的树,b、c、d、e分别为其四个儿子节点, b >= c >= d >= e。

由题意,应将根的任一子树同其他相同父亲的子树合并,即merge(son[fa] [x])。

所以合并a的左右子树,其中将b、c合并,又因为b >= c,所以c被抵消,保留b作为价值。

同理d、e合并,抵消较小的e,保留d作为价值。

因为a与自己的任一子节点都有瓜葛,所以它不和任何点合并,直接最后加入集合。

Proves

按照Sol方案,val_1 = max(b, c) + max(d, e) = b + d

反之,若b与e合并,则c只能与d合并。

则val_2 = max(b, e) + max(c, d) = b + c

因为b >= c >= d >= c

所以b + d >= b + c

所以val_1 <= val_2,即Sol方案更优。

因此可证每个点定和它对应子树的__同等大小级别的点__抵消,为最优解。

Details

  1. 根节点在子树合并完后最后加入集合,不和任何点合并抵消。

  2. 较大集合会有剩下的点,直接加入集合。

  3. merge时 不能swap(x, y) ,而必须 swap(pq[x], pq[y]) ,否则会影响dfs中pq[x].push(a[x])加入集合。

  4. 时间复杂度证明:

    每次合并耗费min(size_b, size_c)的时间复杂度(size_b、size_c为两个同级子树大小)。

    一路合并上去,每合并后一颗树的size将缩减为其子树size的最大值,则近似为长链剖分的O(n)时间复杂度

    再算上优先队列O(log(n)),则总时间复杂度O(n · log(n))。

    不同于普通启发式合并不删只并,此题是删除了部分点后合并的,所以均摊合并两点时间复杂度O(1)。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    inline int read() {
        int x = 0; char ch = getchar(); bool sgn = 0;
        while (ch < '0' || ch > '9') sgn |= ch == '-', ch = getchar();
        while (ch >= '0' && ch <= '9') x = x * 10 + (ch & 15), ch = getchar();
        return sgn ? -x : x;
    }
    const int MN = 2e5 + 10;
    int n, a[MN];
    int cur, h[MN], nxt[MN], ver[MN];
    vector<int> tmp;
    priority_queue<int> pq[MN];
    long long ans;
    inline void add_edge(int x, int y) {
        cur++;
        ver[cur] = y, nxt[cur] = h[x], h[x] = cur;
    }
    inline void merge(int x, int y) {
        if (pq[x].size() < pq[y].size()) swap(pq[x], pq[y]);
        while (!pq[y].empty()) {
            tmp.push_back(max(pq[x].top(), pq[y].top()));
            pq[x].pop(), pq[y].pop();
        }
        while (!tmp.empty()) pq[x].push(tmp.back()), tmp.pop_back();
    }
    void dfs(int x) {
        for (int i = h[x]; i; i = nxt[i]) {
            int y = ver[i];
            dfs(y), merge(x, y);
        }
        pq[x].push(a[x]);
    }
    signed main() {
        n = read();
        for (int i = 1; i <= n; ++i) a[i] = read();
        for (int i = 2, fa; i <= n; ++i) {
            fa = read();
            add_edge(fa, i);
        }
        dfs(1);
        while (!pq[1].empty()) ans += pq[1].top(), pq[1].pop();
        cout << ans << '\n';
        return 0;
    }
    

Data

Code:https://www.luogu.com.cn/blog/xht37/p5290-shi-er-xing-lian-kao-2019-chun-jie-shi-er-xiang

思路 & 正确性证明:https://www.luogu.com.cn/blog/TheLostWeak/solution-p5290

时间复杂度证明:https://www.luogu.com.cn/blog/wufeiyang/solution-p5290

posted @ 2022-06-08 18:18  zjsqwq  阅读(87)  评论(0)    收藏  举报