(树形dp)Spring tree

1|0题目链接

Spring tree

2|0题目概述

给定n个铁球,重量为wi,再给定n - 1条弹簧(可变的边权)所链接的两端,每个位置上的铁球可以相互交换。弹簧的长度为每个节点的子树边权和+1。问从根节点(1节点)开始的最大深度。

输入 #1

4 1 2 3 4 1 2 2 3 3 4

输出 #1

23

样例说明

In the test case, keep original arrangement is a good idea. maximum depth = (1+4) + (1+4+3) + (1+4+3+2) = 23

3|0思路

首先我们发现,更重的球会更倾向于放到深度最深的位置。

枚举最优解中深度最深的叶子 leaf,那么最优解会怎样放置呢?

首先最重的球一定给 leaf,然后考虑 leaf 的父亲 parent(leaf),那么最重的 size(parent(leaf)) 个球一定都在 parent(leaf) 的子树中(其中 size(u) 表示 u 的子树大小)..... 依次类推。

对球的重量排序,用 w(k) 表示 k 个最重的球的总重量。

那么我们可以把节点 u 的权值设成 w(size(u)) 接着求出树上从根到树叶找出一条权值和最大的路径即可,这个可以用 O(n) 的 DP 来实现,需要注意的是根节点没有连向父亲的弹簧,所以根节点权值应该设成 0.

4|0AcCode:

#include <bits/stdc++.h> using namespace std; #define sp ' ' #define endl '\n' #define push_back pb typedef long long ll; typedef vector<int> vi; typedef pair<int, int> PII; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const int N = 1e5 + 10; int n, m, cnt, ans; int tree[N], siz[N], sum[N]; vi g[N]; vi w; void dfs1(int u, int fa){ siz[u] = 1; for(auto v : g[u]){ if(v != fa) dfs1(v, u); siz[u] += siz[v]; } } void dfs2(int u, int fa){ int res = 0; for(auto v : g[u]){ if(v != fa){ dfs2(v, u); res = max(res, tree[v] + sum[siz[v]] + 1); } } tree[u] = res; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cin >> n; rep(i, 1, n){ int a; cin >> a; w.pb(a); } sort(all(w), greater<int>()); rep(i, 1, n) sum[i] = sum[i - 1] + g[i]; rep(i, 1, n - 1){ int u, v; cin >> u >> v; g[u].pb(v); g[v].pb(u); } dfs1(1, -1); dfs2(1, -1); cout << tree[1] << endl; return 0; }

__EOF__

本文作者ReSakura
本文链接https://www.cnblogs.com/ReSakura/p/16342946.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ReSakura  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示