HDU6446
题解
- 任何一种排列在树上都可以实现。那么我么固定两个结点i和j,记dis(i,j)为i和j的最短距离。那么从i和j放置数,有2*(n-1)种情况。剩下的n-2个点有(n-2)!种情况。所以一共有2*(n-1)!种情况。所以枚举i和j,求sum(dis(i,j)) * 2 * (n-1)!的和。
- sum(dis(i,j))可以树形DP解决,即求树上任意两点之间的距离和。找一个结点u,u的儿子为v,那么u和v分树为两部分,设一部分大小为sz[v],另一部分就为n - sz[v]。从一部分的一个结点到另一部分的一个节点必然经过边e,所以ans += sz[v] * (n - sz[v]) * dis
代码
#include <bits/stdc++.h>
using namespace std;
int const N = 100000 + 10;
typedef long long ll;
int const MOD = 1e9 + 7;
int n,first[N],ne[N<<1],to[N<<1],tot;
ll f[N],len[N<<1],sz[N],ans;
void add(int u,int v,ll l){
ne[tot] = first[u];
to[tot] = v;
len[tot] = l;
first[u] = tot++;
}
void pre(){
f[0] = 1;
for(int i=1;i<N;i++)
f[i] = f[i-1] * i % MOD;
}
void dfs(int u,int fa){
sz[u] = 1;
for(int i=first[u];i!=-1;i=ne[i]){
int v = to[i];
int dis = len[i];
if(v == fa) continue;
dfs(v,u);
sz[u] += sz[v];
ans = (ans + sz[v] * (n - sz[v]) * dis) % MOD;
}
}
int main(){
pre();
while(~scanf("%d",&n)){
tot = ans = 0;
memset(first,-1,sizeof(first));
memset(sz,0,sizeof(sz));
for(int i=1;i<n;i++){
int u,v;
ll l;
scanf("%d%d%lld",&u,&v,&l);
add(u,v,l); add(v,u,l);
}
dfs(1,-1);
printf("%lld\n",2 * f[n-1] * ans % MOD);
}
return 0;
}