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 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”