Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1581 Accepted Submission(s): 588
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个数排列后算出所有排列的路径距离和(比如有3个顶点的数,全排列为123,132,213,231,312,321),然后结果就是所有排列的结果(比如123这个排列的结果就是顶点1到顶点2的距离加上顶点2到顶点3的距离)加起来,如果顺着题意这样阶梯的话暴力会超时,我们只需要转化一下就可以:题意要求所有排列的结果,我们只需要想每条边的距离在所有全排列中共用了几次,然后乘以这条边的权重就是这条边在所有排列中所贡献的距离和,其他边同理,接下来通过排列的数学知识我们知道任意两个相邻顶点的所有排列共出现了(n-1)!*2的次(乘以2是因为相邻的两个顶点可以交换顺序),这样的话我们的问题就转化成在这个树中任意两个顶点之间最短距离的和,最后乘以(n-1)!*2就是我们要的答案。 |
#include<vector>
#include<string.h>
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
typedef long long int LL;
const int MOD = 1e9 + 7;
LL jc[N];
LL son[N];
struct Edge {
int v;
int w;
}edge;
vector<Edge>ve[N];
int n;
LL dfs(int current, int father,int pre_dis) {//current代表当前节点,father父节点,pre_dis代表父节点与当前节点之间的权重
LL result=0;
LL juli;
for (int i = 0; i < ve[current].size(); i++) {
int next = ve[current][i].v;//记录与current节点相邻的节点
juli = ve[current][i].w;
if (next ==father)
continue;
result+=dfs(next, current,juli);
son[current] += son[next] + 1;//当前节点等于子节点的个数+子节点它本身(1)
}
//当所有节点遍历完之后统计每条边被用的次数
result+=(((son[current] + 1)%MOD)*(n - (son[current] + 1))%MOD) % MOD*(pre_dis) % MOD;
return result;
}
int main() {
freopen("Text.txt", "r", stdin);
jc[1] = 1;
for (int i = 2; i <= 1e5; i++) {//存取n个顶点的阶乘
jc[i] = (jc[i - 1] * i)%MOD;
}
while (cin >> n) {
if (n == 1)
cout << 0 << endl;
else {
memset(son, 0, sizeof(son));
for (int i = 0; i <= 1e5; i++) {//注意每次while循环清空不定数组
ve[i].clear();
}
int v1, v2, dis;
for (int i = 1; i < n; i++) {
scanf("%d%d%d", &v1, &v2, &dis);//这个地方一定要用scanf读取数据,用cin会超时
ve[v1].push_back(Edge{ v2,dis });
ve[v2].push_back(Edge{ v1,dis });
}
printf("%lld\n", (((dfs(1, 0,0) * 2) % MOD)*jc[n - 1]) % MOD);
}
}
return 0;
} |