题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2376
思路:
引:如果暴力枚举两点再求距离是显然会超时的。转换一下思路,我们可以对每条边,求所有可能的路径经过此边的次数:设这条边两端的点数分别为A和B,那 么这条边被经过的次数就是A*B,它对总的距离和的贡献就是(A*B*此边长度)。我们把所有边的贡献求总和,再除以总路径数N*(N-1)/2,即为最 后所求。
每条边两端的点数的计算,实际上是可以用一次dfs解决的。任取一点为根,在dfs的过程中,对每个点k记录其子树包含的点数(包括其自身),设点数为a[k],则k的父亲一侧的点数即为N-a[k]。这个统计可以和遍历同时进行。故时间复杂度为O(n)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 #define MAXN 10000+10 9 struct Node{ 10 int v,len; 11 }; 12 vector<Node>vet[MAXN]; 13 int n; 14 double dp[MAXN]; 15 int sum[MAXN]; 16 17 void dfs(int root,int father){ 18 sum[root]=1; 19 for(int i=0;i<vet[root].size();i++){ 20 int son=vet[root][i].v; 21 int len=vet[root][i].len; 22 if(son==father)continue; 23 dfs(son,root); 24 sum[root]+=sum[son]; 25 dp[root]+=dp[son]+(sum[son]*(n-sum[son]))*(double)len; 26 } 27 } 28 29 int main(){ 30 // freopen("1.txt","r",stdin); 31 int _case,u,v,len; 32 scanf("%d",&_case); 33 while(_case--){ 34 scanf("%d",&n); 35 for(int i=0;i<=n;i++)vet[i].clear(); 36 for(int i=1;i<=n-1;i++){ 37 scanf("%d%d%d",&u,&v,&len); 38 Node p1,p2; 39 p1.v=v,p2.v=u; 40 p1.len=p2.len=len; 41 vet[u].push_back(p1); 42 vet[v].push_back(p2); 43 } 44 memset(sum,0,sizeof(sum)); 45 memset(dp,0,sizeof(dp)); 46 dfs(0,-1); 47 int s=(n*(n-1)/2); 48 printf("%1lf\n",(double)dp[0]/s); 49 } 50 return 0; 51 }