1. 题面梗概
中译中:给出一棵 n 个点的树,根为 1。定义子树 S(r) 的代价为 ∑u∈S(r)au⋅d(r,u)。对每个 r∈[1,n],允许在 S(r) 内执行至多一次操作:断开某棵子树 S(u)(u=r),将其接到 S(r) 内剩余的任意节点 v 上。求操作后 S(r) 的最大代价。
n≤2⋅105,ai≤2⋅105,∑n≤2⋅105
2. 逻辑推导
设我们把子树 S(u) 移动到节点 v,其贡献值为:
Δ(u,v)=sz_sum[u]⋅(d(r,v)+1−d(r,u))
若 p 是 u 的父节点,将子树 S(p) 移动到节点 v 的总贡献值:
Δ(p,v)=sz_sum[p]⋅(d(r,v)+1−d(r,p))
因为 sum[u]<sum[p],d(r,u)>d(r,p),所以 Δ(p,v)>Δ(u,v)。
结论:如果要移动以 r 为根的子树,最优的移动对象 u 一定是 r 的直接子节点。
2.1 状态设计
基于上述性质,我们只需要在每个节点维护两个状态即可。
- f[u]:不进行任何操作时,S(u) 的原始代价。
- dp[u]:在 S(u) 内最多执行一次操作后的最大代价。
转移:
- 操作在子树内:即操作在某个儿子 c 的子树内完成。
dp[u]=f[u]+max(dp[c]−f[c])
- 操作就在 u 点:即选择一个儿子 c,将 S(c) 断开接到 S(u)∖S(c) 中最深的点上。
dp[u]=max(sz_sum[c]⋅(depth−dep[c]+1))
depth 为 S(u)∖S(c) 中最大的深度
2.2 优化
为了快速查询 depth,我们维护每个节点到最深节点的路径,并区分最深路径和次深路径。
若子树在最深路径上,则 depth 就在次深路径上。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #include <bits/stdc++.h> using namespace std; using ll = long long; using PII = pair<int, int>;
const int maxn = 2e5 + 5;
vector< int > adj[maxn]; ll a[maxn], sz_sum[maxn], f[maxn], dp[maxn]; int dep[maxn], mx1[maxn], mx2[maxn], son[maxn]; int n;
void dfs( int u, int p ) { sz_sum[u] = a[u]; dep[u] = dep[p] + 1; f[u] = 0; mx1[u] = dep[u]; mx2[u] = 0; son[u] = 0;
for( int v : adj[u] ) { if( v == p ) continue; dfs( v, u ); sz_sum[u] += sz_sum[v]; f[u] += ( f[v] + sz_sum[v] ); if( mx1[v] > mx1[u] ) { mx2[u] = mx1[u]; mx1[u] = mx1[v]; son[u] = v; } else if( mx1[v] > mx2[u] ) { mx2[u] = mx1[v]; } } }
void dfs2( int u, int p ) { ll delta = 0;
for( int v : adj[u] ) { if( v == p ) continue; dfs2( v, u ); delta = max( delta, dp[v] - f[v] ); }
dp[u] = f[u] + delta;
for( int v : adj[u] ) { if( v == p ) continue; ll depth = ( v == son[u] ) ? max( mx2[u], dep[u] ) : mx1[u]; ll cur = sz_sum[v] * ( depth - dep[v] + 1 ); dp[u] = max( dp[u], f[u] + cur ); } }
void solve( ) { cin >> n; for( int i = 1; i <= n; ++ i ) { cin >> a[i]; adj[i].clear( ); }
for( int i = 1; i < n; ++ i ) { int u, v; cin >> u >> v; adj[u].push_back( v ); adj[v].push_back( u ); }
dep[1] = 0; dfs( 1, 0 ); dfs2( 1, 0 );
for( int i = 1; i <= n; ++ i ) cout << dp[i] << ' '; cout << '\n'; }
int main( ) { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _t = 1; cin >> _t; while( _t -- ) solve( ); return 0; }
|
4. 复盘