Tree and Permutation(2018 CCPC网络赛)

Tree and Permutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1030    Accepted Submission(s): 369


Problem Description

There are N vertices connected by N−1 edges, each edge has its own length.
The set { 1,2,3,…,N } contains a total of N! unique permutations, let’s say the i-th permutation is Pi and Pi,j is its j-th number.
For the i-th permutation, it can be a traverse sequence of the tree with N vertices, which means we can go from the Pi,1-th vertex to the Pi,2-th vertex by the shortest path, then go to the Pi,3-th vertex ( also by the shortest path ) , and so on. Finally we’ll reach the Pi,N-th vertex, let’s define the total distance of this route as D(Pi) , so please calculate the sum of D(Pi) for all N! permutations.

 Input

There are 10 test cases at most.
The first line of each test case contains one integer N ( 1≤N≤105 ) .
For the next N−1 lines, each line contains three integer X, Y and L, which means there is an edge between X-th vertex and Y-th of length L ( 1≤X,Y≤N,1≤L≤109 ) .

Output

For each test case, print the answer module 109+7 in one line.

Sample Input

3

1 2 1

2 3 1

3

1 2 1

1 3 2

Sample Output

16

24

Source

2018中国大学生程序设计竞赛 - 网络选拔赛

 题意:给你一棵树,有n个点,n-1条边,每条边都有一个权值,有n!种方式遍历这棵树,求所有遍历方式的距离之和

分析:考虑每条边的贡献,对于一条边a->b,长度为L,设该条边把树分为两部分,一部分节点数为M,那么另一部分节点数为N-M,则该条边两边所连的点方案总数为N*(N-M),此时该边所连的两端的点已经确定,那么还剩下N-2个点,有(N-2)!的排列方式,而那条边所连的两个点当成整体有(N-1)个空可以插(插空法),最后还要考虑那两个点内部排列,所以要乘以2,综上所述,ans=2*(N-M)*M*L*(N-2)!*(N-1)=2*M(N-M)*L*(N-1)!

ac code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
vector<int>edge[maxn];
typedef long long ll;
const ll mod=1e9+7;
struct Edge{
int u,v,w;
}ed[maxn];
int num[maxn];
void dfs(int now,int pre)
{
    num[now]=1;
    for(int i=0;i<edge[now].size();i++)
    {
        int v=edge[now][i];
        if(v==pre) continue;
        dfs(v,now);
        num[now]+=num[v];///计算以now为根的子树有多少个节点
    }
}
ll f[maxn];
int main()
{
    int n;
    int u,v,w;
    f[1]=f[0]=1;
    for(int i=2;i<=100000;i++)
        f[i]=(f[i-1]*i)%mod;///计算阶乘
    while(~scanf("%d",&n)){
        memset(num,0,sizeof num);
        for(int i=1;i<=n;i++)
         edge[i].clear();///注意每次都要清空
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            ed[i].u=u;
            ed[i].v=v;
            ed[i].w=w;
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        dfs(1,0);
        ll ans=0;
        for(int i=1;i<n;i++)///计算每条边的贡献
        {
            int t1=num[ed[i].u];
            int t2=num[ed[i].v];
            int t3=ed[i].w;
            if(t1<t2) swap(t1,t2);///保证t2小些,防止t1或t2有一个为根节点的子树时另一个一减就会为0
            ans=(ans+(1ll*2*t2*(n-t2)*t3)%mod*f[n-1])%mod;///那个1LL很关键,不加就会爆
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/shaohang_/article/details/82112654