Tree and Permutation

本文探讨了在树形结构中使用动态规划(DP)解决排列组合问题的方法,特别是如何计算所有可能排列下,从一个顶点到另一个顶点经过的边的总权重之和。文章介绍了通过树形DP优化计算过程,避免了暴力求解的超时问题,并详细解释了如何利用排列组合的数学知识简化计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;

}

 

 
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值