HDU 6446 Tree and Permutation
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
题目链接
有一棵树,n个节点,n-1条边,求n个节点全排列后从第一个节点按照顺序依次向下一个节点走的最短路径之和。
从树的任一节点开始dfs(宽度优先搜索),用数组sum记录其节点下的所有节点(包括自己)个数,以此计算任意两点间的最短距离之和。
第一个样例:
3
1 2 1
2 3 1
构建的树为:
在深度优先搜索中,由节点1递归搜索到叶子节点3之后回溯到节点2,这时节点2的子节点记录数组sum[2]更新为2(节点2和节点3),计算sum[3]×(n-sum[3])×length[2][3]=1×2×1=2加入到结果中,此计算式的意义为:2节点下面正在遍历的节点3的sum[3](节点3的子节点数量)乘不为节点3子节点的所有节点数量(边2-3总共需要统计的数量)乘边2-3的权值,在这个图里的意义为节点3到节点2和节点1两条路径都需要走2-3这条边,所以这条边进行两次计数,计数之后再回溯到节点1,这时节点1的子节点记录数组sum[1]更新为3(节点1、节点2和节点3),计算sum[2]×(n-sum[2])×length[2][3]=2×1×1=2加入到结果中,此计算式的意义为1节点下面正在遍历的节点2的sum[2](节点2的子节点数量)乘不为节点2子节点的所有节点数量(边1-2总共需要统计的数量)乘边1-2的权值,在这个图里的意义为节点2和节点3到节点1都需要走1-2这条边,所以对这条边进行两次计数,计数之后返回结果4,4的含义就是1-2的最短路(1)+1-3的最短路(2)(边1-2两次计数中的一次+边2-3两次计数中的一次)+2-3的最短路(1)。
另一个样例:
3
1 2 1
1 3 2
构建的树为:
在深度优先搜索中由节点1递归搜索到节点2回溯到节点1,此时sum[1]的子节点记录数组更新为2(节点1和节点2),计算sum[2]×(n-sum[2])×length[1][2]=1×2×1=2加入到结果中,此计算式的意义为:1节点下面正在遍历的节点2的sum[2](节点2的子节点数量)乘不为节点2子节点的所有节点数量(边1-2总共需要统计的数量)乘边1-2的权值,在这个图里的意义为节点2到节点1和节点2两条路径都需要走1-2这条边,所以对这条边进行两次计数,进行节点1下一个子节点的遍历,递归搜索到节点3回溯到节点1,此时sum[1]的子节点记录数组更新为3(节点1、节点2和节点3),计算sum[3]×(n-sum[3])×length[1][3]=1×2×2=4加入到结果中,此计算式的意义为节点1下面的正在遍历的节点3的sum[3](节点3的子节点数量)乘不为节点3子节点的所有节点数量(边1-3总共需要统计的数量)乘边1-3的权值,在这个图里的意义为节点3到节点1和节点2都需要走1-3这条边,所以对这条边进行两次计数,计数之后返回结果6,6的含义就是1-2的最短路(1)+1-3的最短路(2)+2-3的最短路(3)(边1-2两次计数中的一次+边1-3两次计数中的一次)。
深度优先搜索统计完任意两点最短路之和之后需要计算在全排列中这个最短路总共使用了多少次,写一个全排列程序统计1和2在不同总数相邻出现的次数(1-2最短路在全排列中走了多少次):
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char *argv[]) {
for (int i = 2; i < 12; ++i) {
vector<int> num(i);
for (int j = 1; j <= i; ++j) {
num[j - 1] = j;
}
int cnt = 0;
do {
for (int j = 1; j < i; ++j) {
if ((num[j] == 1 && num[j - 1] == 2) || (num[j] == 2 && num[j - 1] == 1)) {
cnt++;
}
}
} while(next_permutation(num.begin(), num.end()));
printf("cnt[%d]=%d\n", i, cnt);
}
return 0;
}
程序输出:
cnt[2]=2
cnt[3]=4
cnt[4]=12
cnt[5]=48
cnt[6]=240
cnt[7]=1440
cnt[8]=10080
cnt[9]=80640
cnt[10]=725760
cnt[11]=7257600
易观察得规律:
Cnt = 2;
for (int i = 3; i <= n; ++i) {
Cnt = Cnt * (i - 1);
}
将最短路之和与需要计算的次数相乘即为最终结果。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
struct Link {
int V, Weight, Next;
Link(int _V = 0, int _Weight = 0, int _Next = 0): V(_V), Weight(_Weight), Next(_Next) {}
};
int N;
int Tot;
long long Ans, Cnt;
Link edges[maxn << 1];
int Head[maxn];
long long Sum[maxn];
void AddEdge(int U, int V, int Weight) {
edges[Tot].V = V;
edges[Tot].Weight = Weight;
edges[Tot].Next = Head[U];
Head[U] = Tot++;
}
void Dfs(int Vertex, int Father) {
Sum[Vertex] = 1;
for (int i = Head[Vertex]; i != -1; i = edges[i].Next) {
if (edges[i].V != Father) {
Dfs(edges[i].V, Vertex);
Sum[Vertex] += Sum[edges[i].V];
Ans = (Ans + Sum[edges[i].V] * (N - Sum[edges[i].V]) * edges[i].Weight % mod) % mod;
}
}
}
int main(int argc, char *argv[]) {
while (~scanf("%d", &N)) {
Tot = 0;
memset(Head, -1, sizeof(Head));
for (int i = 1, U, V, C; i < N; ++i) {
scanf("%d%d%d", &U, &V, &C);
AddEdge(U, V, C);
AddEdge(V, U, C);
}
memset(Sum, 0, sizeof(Sum));
Ans = 0;
Dfs(1, -1);
Cnt = 2;
for (int i = 3; i <= N; ++i) {
Cnt = Cnt * (i - 1) % mod;
}
printf("%lld\n", Ans * Cnt % mod);
}
return 0;
}