1. 程式人生 > >HDU 6446 Tree and Permutation (思路+dp)

HDU 6446 Tree and Permutation (思路+dp)

題意
給你n個點,n-1條邊的一棵樹,現在有一個從 ( 1... n ) 的全排列,表示的是你在這棵樹上要走的路線,現在問你走完這些全排列的所有權值和是多少
思路
先看

( 1 , 2 , 3 ) 你會發現有 ( 1
, 2 , 3 )
, ( 1 , 3 , 2
)
, ( 2 , 1 , 3 ) , ( 2 , 3 , 1 ) , ( 3 , 1 , 2 ) , ( 3 , 2 , 1 ) ,你會發現 ( 1 , 3 ) ( 1 , 2 ) ( 3 , 2 ) ( 3 , 1 ) . . . . 他們出現的次數都是相同的都是由此我們可以看出,對於一個全排列來說,所有邊出現的次數都是相同的,那麼我們就可以知道全排列的權值其實就等於樹上任意點到所有點的和乘上這兩個點組成的邊的次數就好了。對於有 n 個點的全排列來說,任意兩點出現了多少次其實是: 首先你將兩個點綁在一起剩下 n 1 個點,之後這個新的點有 ( n 1 ) 种放置的方法,之後剩餘 ( n 2 ) 個點隨意放置是個 ( n 2 ) ! 種方法所以共有 ( n 1 ) ( n 2 ) ! = ( n 1 ) ! 種方法,那麼對於樹上任意點到所有點的距離和,我們可以dfs一下就能得到了.記得最後的答案要成2
程式碼

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int mod = 1e9+7;
int n;
long long dp[maxn] , sum[maxn] , jc[maxn];
vector<tuple<int,int> >V[maxn]; 
void dfs(int u,int fa)
{
    int v,w;
    sum[u] = 1;
    for(auto &i : V[u])
    {
        tie(v,w) = i;
        if(v == fa) continue;
        dfs(v,u);
        sum[u] += sum[v];
        dp[u] = ((dp[u] + dp[v]) % mod + (sum[v] * (n-sum[v]) % mod) * w) % mod;
    }
}
void init()
{
    jc[0] = 1, jc[1] = 1;
    for(int i = 2 ; i <= maxn ; i++)
    {
        jc[i] = jc[i-1] * i % mod; 
    }
}
int main()
{
    init();
    while(scanf("%d",&n)!=EOF)
    {
        for(int i = 0 ; i <= n ; i++) V[i].clear();
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        int x,y,z;
        for(int i = 0 ; i < n-1 ; i++) 
        {
            scanf("%d%d%d",&x,&y,&z);
            x--,y--;
            V[x].emplace_back(y,z);
            V[y].emplace_back(x,z);
        }
        dfs(0,-1);
        long long ans = dp[0] * jc[n-1] % mod * 2 % mod;
        cout<<ans<<endl;
    }
}