题意:
给一颗树,每个节点有人数,边有权值,表示经过这条边所需时间,问取某个节点作为开会地点,所有人全部到达此节点开会的总时间最小,输出这个总时间。
思路:
树形DP:定义dp[u]表示以u根的子树上所有点到点u开会的答案,ps[u]表示以u为根的子树所有点上的人数和。
根据数据定义可以简单推得方程:dp[u] = Σ(dp[v]+ps[v]*w)。
使用树的旋转可以慢慢求得到各点开会的结果,取其中的最小值作为答案。
C++代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100010;
const int maxm = 200010;
int n,tol,head[maxn];
struct edge
{
int to,cost,next;
}es[maxm];
void addedge( int u , int v , int w )
{
es[tol].to = v;
es[tol].cost = w;
es[tol].next = head[u];
head[u] = tol++;
}
LL p[maxn],ps[maxn],dp[maxn],ans;
void dfs1( int u , int f )
{
dp[u] = 0;
ps[u] = p[u];
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to; LL w = es[i].cost;
if ( v!=f )
{
dfs1 ( v , u );
ps[u] += ps[v];
dp[u] += dp[v]+ps[v]*w;
}
}
}
void dfs2( int u , int f )
{
ans = min( ans , dp[u] );
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to; LL w = es[i].cost;
if ( v!=f )
{
LL tmp = dp[u]-dp[v]-ps[v]*w;
LL pps = ps[u]-ps[v];
dp[v] += tmp+pps*w;
ps[v] += pps;
dfs2 ( v , u );
}
}
}
int main()
{
while ( scanf ( "%d" , &n )==1 )
{
for ( int i=1 ; i<=n ; i++ )
scanf ( "%lld" , &p[i] );
tol = 0;
memset ( head , -1 , sizeof(head) );
for ( int i=1 ; i< n ; i++ )
{
int u,v,w;
scanf ( "%d%d%d" , &u , &v , &w );
addedge ( u , v , w );
addedge ( v , u , w );
}
ans = 1e18;
dfs1( 1 , 0 );
dfs2( 1 , 0 );
printf ( "%lld\n" , ans );
}
return 0;
}