HDU6446 Tree and Permutation

题目链接:https://cn.vjudge.net/problem/HDU-6446

知识点:  组合数学

解题思路:

  对于树上的一条边,设其两端的点数为 $x, y$,边长为 $L$. 对于一个排列,总共会行动 $n-1$ 次,一次行动如果要经过这一条边,那么这次行动的起点和终点一定要在这条边的不同的两端,对应的排列数为 $xy(n-2)!$ . 由于边长为 $L$, 有 $n-1$ 次行动,还要考虑正反(再乘个 $2$),所以最终这条边对答案的贡献为$2xyL(n-1)!$ .

  计算一条边两端的点数用一次 $dfs$ 即可。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int MAXN=1e5+5;
 5 const LL MOD=1e9+7;
 6 vector<int> G[MAXN];
 7 int fa[MAXN];
 8 int have[MAXN];
 9 void dfs(int rt,int last){
10     have[rt]++;
11     for(int i=0;i<G[rt].size();i++){
12         int v=G[rt][i];
13         if(v==last) continue;
14         dfs(v,rt);
15         have[rt]+=have[v];
16         fa[v]=rt;
17     }
18 }
19 struct Edge{
20     int u,v;
21     LL val;
22 }e[MAXN];
23 LL power[MAXN];
24 void init(){
25     power[0]=1;
26     for(LL i=1;i<MAXN;i++)
27         power[i]=power[i-1]*i%MOD;
28 }
29 int main(){
30     init();
31     int N;
32     while(scanf("%d",&N)==1){
33         memset(have,0,sizeof(have));
34         for(int i=1;i<=N;i++)   G[i].clear();
35         for(int i=1;i<N;i++){
36             scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].val);
37             G[e[i].u].push_back(e[i].v);
38             G[e[i].v].push_back(e[i].u);
39         }
40         dfs(1,0);
41         LL ans=0;
42         for(int i=1;i<N;i++){
43             if(fa[e[i].u]==e[i].v)
44                 ans=(ans+power[N-1]*have[e[i].u]%MOD*(N-have[e[i].u])%MOD*e[i].val%MOD)%MOD;
45             else
46                 ans=(ans+power[N-1]*have[e[i].v]%MOD*(N-have[e[i].v])%MOD*e[i].val%MOD)%MOD;
47         }
48         printf("%lld\n",ans*2%MOD);
49     }
50 
51     return 0;
52 }

 

posted @ 2018-08-26 00:19  Blogggggg  阅读(222)  评论(0编辑  收藏  举报