题意:将树上的所有点移动到根节点上,求最小花费
关键数据范围:2⩽n⩽105
解题思路:由题意,这 n 个节点一定构成树状结构,所以我们可以任选一个节点作为根
对于此题目,其实使用最简单的DFS就可以切掉,只需要以任意节点为根,DFS让子节点向上转移并增加ans即可
但在这之前,我们需要证明<对任意子树,从子节点移动向叶节点一定是最优方案>,我们使用*微扰(临项交换)*进行证明
Details
已知节点A与其左儿子B右儿子C,B有x个电子,C有y个电子,AB路径的花费为a,AC路径的花费为b证明由B,C移动向A造成的开销一定最小,即ans1=abs(a×x)+abs(b×y)最小若选择其中一点先行移动到根节点再在另一儿子节点道路上往来则开销为ans2=abs((a+b)×x)+abs(b×abs(x+y))ORans3=abs((a+b)×y)+abs(a×abs(x+y))<1>若x,y带有相同电子,即x,y同号,可得:ans1⩽ans2当且仅当x=0时取等ans1⩽ans3当且仅当y=0时取等ans2<ans3==abs(x)>abs(y)<2>若x,y带有不同电子,即x,y异号,可得:ans1<ans2,ans1<ans3ans2<ans3==abs(x)<abs(y)<3>若x,y代数和为0,可得:ans1<ans2=ans3综上所述,改变移动的方式不会使结果变得更优
AC代码
Details
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair< ll,ll >
#define to first
#define cost second
const int N=1e5+10;
ll ans=0;
ll t[N];
vector< pii >rode[N];
bool vis[N];
void dfs( ll st )
{
vis[st]=true;
for( auto x:rode[st] )
{
if( vis[x.to] )
{
continue;
}
dfs( x.to );
ans+=abs( t[x.to]*x.cost );
t[st]+=t[x.to];
}
}
int main() {
int n;
scanf( "%d",&n );
for( int i=1;i<=n;i++ )
{
scanf( "%lld",&t[i] );
}
for( int i=1;i<n;i++ )
{
ll u, v, w;
scanf( "%lld%lld%lld",&u ,&v ,&w );
rode[u].push_back( { v,w } );
rode[v].push_back( { u,w } );
}
dfs( 1 );
printf( "%lld",ans );
return 0;
}
时间复杂度:O(n)