【Good Bye 2020 D】13th Labour of Heracles

题目链接

链接

翻译

让你给一棵树上的边染色,然后对于颜色为 \(k\) 的边, 只保留这些边,形成一个 \(k\) 子图,然后

不是可能会组成很多个联通分量吗?则这个 \(k\) 子图的权重就为所有联通分量的权重最大值。(联通分量的权值为各个节点加和)

你最多可以使用 \(K\) 种颜色的边,\(K\)\(1\)\(n-1\) 变换,让你求出所有子图的权重的累加和的最大值。

题解

假设某种最优方案,出现了其中某一个 \(k\) 子图是由两个或更多联通分量取最大值得到的。

那么我们总是可以将权重和较小的那个联通分量(较小者对答案无贡献)改成相邻颜色的边,让其他子图的权重和更大一些。

所以,最后的最优结果中,每个 \(k\) 子图都只有一个联通分量。

初始的时候,每条边的颜色都相同,每个节点的权重对答案只贡献一次。然后我们在此基础上修改边的颜色,会发现每修改

一条边的话,达到的效果就是将边的一端的节点权重加上去,如下图,我们把边 \(1\) 改成颜色 \(2\),然后将另外一端颜色

\(1\) 的边也改成相邻的 \(2\) (上面那个推论),这样节点 \(v\) 的权重在最后算累加的时候就又会额外加一次了。

那么我们只需要每次将权重最大的节点的权重累加一次就好了,能累加几次呢?节点的度数减去 \(1\) !为啥要减 \(1\)? 一开始

颜色都一样,已经累加过一次了!

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 1e5;

int T, n;
int du[N + 10],w[N + 10];
multiset<int,greater<int> > myset;

int main() {
	#ifdef LOCAL_DEFINE
		freopen("in.txt", "r", stdin);
	#endif
	ios::sync_with_stdio(0), cin.tie(0);
    cin >> T;
    while (T--){
        myset.clear();
        cin >> n;
        LL ans = 0;
        for (int i = 1;i <= n; i++){
            du[i] = 0;
            cin >> w[i];
            ans += w[i];
        }
        for (int i = 1;i <= n - 1; i++){
            int u, v;
            cin >> u >> v;
            du[u]++;du[v]++;
        }
        for (int i = 1;i <= n; i++){
            for (int j = 1;j < du[i]; j++){
                myset.insert(w[i]);
            }
        }
        cout << ans;

        for (int i = 1;i <= n-2; i++){
            int x = *(myset.begin());
            myset.erase(myset.begin());
            ans += x;
            cout << " " << ans;
        }
        cout << endl;

    }
	return 0;
}

posted @ 2021-02-11 08:11  AWCXV  阅读(73)  评论(0编辑  收藏  举报