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
题意:给你一棵树,有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;
}