链接
http://acm.hdu.edu.cn/showproblem.php?pid=6446
题意
给你一棵树 树的边有权值
节点一开始没有序号 你用1-n的全排列给他们编号 编号n!次
对于每次编号
计算 tmp=dist[1][2]+dist[2][3]+dist[3][4]+...+dist[n-1][n];
最后计算sigma(tmp);
题解
先上公式
计算出树上任意两点之间的距离和(双向的,1->2 和 2->1 都要算),设其为sum
Ans=sum*(n-1)!
证明
考虑一条路径x y
那么包含这条路径的排列有多少个?
剩下来的n-2个数排成一排共有(n-2)!种方案
(x,y)插空,有n-1个空,所以一共(n-1)!个方案包括了xy这条路径
至于所有路径之和的计算方法
hdu2376
考虑每一条边,设这条边左边A个节点,右边B个节点
那么一共有(A*B)条路径经过这条边
我们只需要计算出每条边左边的节点数即可 这个可以用树形dp实现
上代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005;
const ll mod=1000000000+7;
ll sum[maxn];
ll dp[maxn];
int n;
struct Edge
{
int v, w;
};
vector<Edge> tree[maxn];
void dfs(int cur, int father)
{
sum[cur] = 1;
for(int i = 0; i < tree[cur].size(); i++)
{
int son = tree[cur][i].v;
ll len = tree[cur][i].w;
if(father == son) continue;
dfs(son, cur);
// sum[cur] += sum[son];
sum[cur] = (sum[cur] + sum[son])%mod;
// dp[cur] += dp[son] + (n-sum[son])*sum[son] * len;
dp[cur] = ((dp[cur]%mod + dp[son]%mod)%mod + (((n-sum[son]) *sum[son])%mod * len) %mod)%mod;
}
}
int main()
{
int u, v, w, T;
while( scanf("%d", &n)!=EOF)
{
for(int i = 0; i < n; i++) tree[i].clear();
memset(sum, 0, sizeof(sum));
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n-1; i++)
{
scanf("%d%d%d", &u, &v, &w);
u--; v--;
Edge t1, t2;
t1.v = v;
t1.w = w;
t2.v = u;
t2.w = w;
tree[u].push_back(t1);
tree[v].push_back(t2);
}
dfs(0, -1);
ll ans=dp[0];
for (ll p=1; p<=n-1; p++) ans=(ans*p)%mod;
ans=(ans*2)%mod;
printf("%lld\n",ans);
}
return 0;
}