题目链接:http://codeforces.com/problemset/problem/294/E
E. Shaass the Great
time limit per test
3.5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
The great Shaass is the new king of the Drakht empire. The empire has n cities which are connected by n - 1 bidirectional roads. Each road has an specific length and connects a pair of cities. There's a unique simple path connecting each pair of cities.
His majesty the great Shaass has decided to tear down one of the roads and build another road with the same length between some pair of cities. He should build such road that it's still possible to travel from each city to any other city. He might build the same road again.
You as his advisor should help him to find a way to make the described action. You should find the way that minimize the total sum of pairwise distances between cities after the action. So calculate the minimum sum.
Input
The first line of the input contains an integer n denoting the number of cities in the empire, (2 ≤ n ≤ 5000). The next n - 1 lines each contains three integers ai, bi and wi showing that two cities ai and bi are connected using a road of length wi, (1 ≤ ai, bi ≤ n, ai ≠ bi, 1 ≤ wi ≤ 106).
Output
On the only line of the output print the minimum pairwise sum of distances between the cities.
Please do not use the %lld specificator to read or write 64-bit integers in C++. It is preferred to use the cin, cout streams or the %I64dspecificator.
Examples
input
3 1 2 2 1 3 4
output
12
input
6 1 2 1 2 3 1 3 4 1 4 5 1 5 6 1
output
29
input
6 1 3 1 2 3 1 3 4 100 4 5 2 4 6 1
output
825
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define LL long long
#define N 5005
#define min(a,b) a<b?a:b
struct edge{
int to;
LL val;
edge(int a, LL b):to(a),val(b){}
edge(){}
};// 结构体存储边的终点和权值
int n;
int fa[N]; // i的父节点
int disfa[N]; // i到父节点的距离
int num[N]; // i树中的结点数
LL sum[N]; // i到i子树所有结点的距离和
vector<edge>g[N];// vector向量存储树的结构
// dfs深度遍历预处理每个结点的父结点
void dfs_fa(int x){
int y;
for(int i=0;i<g[x].size();++i){
y=g[x][i].to;
if(fa[y]==y&&y!=0){
fa[y]=x;
disfa[y]=g[x][i].val;
dfs_fa(y);
}
}
}
// 计算一棵树中某一个结点和其他每个结点的距离之和
void dfs_sum(int x, LL &Sum, LL val){
Sum+=val, num[x]=1;
for(int i=0;i<g[x].size();++i){
int y=g[x][i].to;
if(fa[y]==x){
dfs_sum(y,Sum,val+g[x][i].val);
num[x]+=num[y];
}
}
}
// 利用之前计算出的值,计算一棵树中剩余结点和其他结点的距离之和
void dfs_son(int x, int tot){
for(int i=0;i<g[x].size();++i){
int y=g[x][i].to;
if(fa[y]==x){
// sum[y]=sum[x]-num[y]*g[x][i].val+(tot-num[y])*g[x][i].val;
sum[y]=sum[x]+(tot-2*num[y])*g[x][i].val;
dfs_son(y,tot);
}
}
}
// dfs遍历找出一棵树中sum值最小的结点
void get_min(int x, int &Min){
if(sum[Min]>sum[x]) Min=x;
for(int i=0;i<g[x].size();++i){
int y=g[x][i].to;
if(fa[y]==x) get_min(y,Min);
}
}
int main()
{
int u, v;
LL w;
LL ans=-1;
scanf("%d",&n);
for(int i=1;i<n;++i){
fa[i]=i;
scanf("%d%d%lld",&u,&v,&w);
u--, v--;
g[u].push_back(edge(v,w));
g[v].push_back(edge(u,w));
}
fa[0]=disfa[0]=0;
dfs_fa(0);
for(int i=1;i<n;++i){
// 断开第i个结点和其父节点相连的边
int r=fa[i]; fa[i]=i;
// 计算0/i结点所在子树所有结点到0/i的距离之和
dfs_sum(0,sum[0],0), dfs_sum(i,sum[i],0);
// 用上面计算出的sum[0/i]计算树中其他sum值
dfs_son(0,num[0]), dfs_son(i,num[i]);
int r1=0, r2=i;
// dfs遍历寻找一棵树中sum值最小的结点
get_min(0,r1), get_min(i,r2);
// 将两个sum值记下来
LL sum1=sum[r1], sum2=sum[r2];
// disfa[i]*num[0]*num[i] 两棵树经过断开边的距离之和
// num[0]*sum2 第一棵子树连向第二棵子树所经过第二子树的距离之和
// num[i]*sum1 第二棵子树连向第一棵子树所经过第一子树的距离之和
LL M=(LL)disfa[i]*num[0]*num[i]+(LL)num[0]*sum2+(LL)num[i]*sum1;
LL tmp=0;
// 两棵子树内部结点相连的距离之和,结果要除以二,因为会计算两遍
for(int j=0;j<n;++j) tmp+=sum[j];
M+=tmp/2;
// 最后选取值最小的
if(ans==-1) ans=M;
else ans=min(ans,M);
memset(sum,0,sizeof(sum));
// 恢复原来的父结点
fa[i]=r;
}
cout<<ans;
return 0;
}