codeforces294E. Shaass the Great

题目链接: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 aibi 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值