hdu-2376 Average distance 树上距离问题

版权声明:转载请注明原址,有错误欢迎指出 https://blog.csdn.net/iwts_24/article/details/82220201

题目链接

hdu-2376 Average distance

题目

Average distance

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1374    Accepted Submission(s): 496
Special Judge

 

Problem Description

Given a tree, calculate the average distance between two vertices in the tree. For example, the average distance between two vertices in the following tree is (d01 + d02+ d03 + d04 + d12 +d13 +d14 +d23 +d24 +d34)/10 = (6+3+7+9+9+13+15+10+12+2)/10 = 8.6.

 

Input

On the first line an integer t (1 <= t <= 100): the number of test cases. Then for each test case: 

One line with an integer n (2 <= n <= 10 000): the number of nodes in the tree. The nodes are numbered from 0 to n - 1. 

n - 1 lines, each with three integers a (0 <= a < n), b (0 <= b < n) and d (1 <= d <= 1 000). There is an edge between the nodes with numbers a and b of length d. The resulting graph will be a tree. 

 

 

Output

For each testcase: 

One line with the average distance between two vertices. This value should have either an absolute or a relative error of at most 10-6

 

 

Sample Input

 

1 5 0 1 6 0 2 3 0 3 7 3 4 2

 

Sample Output

 

8.6

 

Source

bapc2007

 

Recommend

lcy   |   We have carefully selected several similar problems for you:  2378 2379 2377 2380 2381 

题意

        求树上任意两点距离的和的平均值。题目描述给出了图的样例,可以参考。

题解

        这个题是树上距离问题的入门题了。跟树状dp有关,算是经典题了。暴力写法肯定是TLE的,这里我们这样分析:

1.题目是求任意2点的距离的和的平均值,那么首先要算任意两点距离的和。图以及权值已经给出,那么我们应该考虑每个边在整体计算的时候出现了几次,然后就与权值相乘,最后再求和。

2.根据这个思路,我们就考虑一个边出现了几次。将边作为中心切开这个图,那么图上的点被分为2部分,假设一部分为n,另一部分为m,那么这个边就被经过了n*m次,因为所有点都要计算一次距离,同时例如0-1 1-0这样的边算经过了一次。且nm满足:m = N-n。那么一个边的权值就是n*m*value

3.那么就考虑利用树状dp,sum数组保存树上边的一边的点的数量,dp数组保存权值的和。那么dp进行到最后,do[root]的值就是距离和了。点的数量很好求,当前结点的数量就是所有孩子结点的数量和,那么权值就用上面的公式就可以了。

4.求出总和基本就算完事了,因为求平均值,所以要除以路线的总数,即:N*(N-1)/2

AC代码 C++ 124ms

#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#include<stack>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 20100
#define INF 0x3f3f3f3f

using namespace std;

int T, N;
LL dp[MAX],sum[MAX];

struct node{
    int to,value;
};

vector<node> vec[MAX];

void init(){
    memset(dp,0,sizeof(dp));
    for(int i = 0;i < N;i++){
        sum[i] = 1;
        vec[i].clear();
    }
}

void tree_dp(int root,int father){
    int len = vec[root].size();
    for(int i = 0;i < len;i++){
        int son = vec[root][i].to;
        if(son == father) continue;
        tree_dp(son,root);
        int value = vec[root][i].value;
        sum[root] += sum[son];
        dp[root] = dp[root] + dp[son] + sum[son]*(N - sum[son])*value;
    }
}

int main() {
    TIME;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &N);
        init();
        int a,b,c;
        for(int i = 0;i < N-1;i++){
            scanf("%d%d%d",&a,&b,&c);
            vec[a].push_back(node{b,c});
            vec[b].push_back(node{a,c});
        }
        tree_dp(0,-1);
        int temp = N*(N - 1) / 2;
        printf("%.6lf\n", dp[0] / (temp*1.0));
    }

    system("pause");
    return 0;
}

注意

1.由于是无向图,所以我们利用vector来储存一个结点的所有孩子结点,但是我们无法确定那个是真正的父结点,所以我们对于每个边2个方向全部存入。判断的时候,如果孩子结点就是父结点的话,说明这个边重复了,就放弃计算。

2.数的根是随便选择的,任意结点作为根都可以。

3.注意hdu的数据是会爆int的,使用long long或者int64。

猜你喜欢

转载自blog.csdn.net/iwts_24/article/details/82220201