天天看点

HDU 6446 Tree and Permutation (树形DP经典)Tree and Permutation

Tree and Permutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 541    Accepted Submission(s): 182

Problem Description

There are N vertices connected by N−1 edges, each edge has its own length.

The set { 1,2,3,…,N } contains a total of N! unique permutations, let’s say the i -th permutation is Pi and Pi,j is its j -th number.

For the i -th permutation, it can be a traverse sequence of the tree with N vertices, which means we can go from the Pi,1 -th vertex to the Pi,2 -th vertex by the shortest path, then go to the Pi,3 -th vertex ( also by the shortest path ) , and so on. Finally we’ll reach the Pi,N -th vertex, let’s define the total distance of this route as D(Pi) , so please calculate the sum of D(Pi) for all N! permutations.

Input

There are 10 test cases at most.

The first line of each test case contains one integer N ( 1≤N≤105 ) .

For the next N−1 lines, each line contains three integer X , Y and L , which means there is an edge between X -th vertex and Y -th of length L ( 1≤X,Y≤N,1≤L≤109 ) .

Output

For each test case, print the answer module 109+7 in one line.

Sample Input

3 1 2 1 2 3 1 3 1 2 1 1 3 2

Sample Output

16 24

Source

2018中国大学生程序设计竞赛 - 网络选拔赛

Recommend

chendu   |   We have carefully selected several similar problems for you:  6447 6446 6445 6444 6443 

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
const int  maxn =1e5+5;
const int mod=1e9+7;

ll gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
ll powmod(ll x,ll y){ll t;for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod;return t;}

/*
题目大意:题目叙述上饶了点弯子,
对于一个排列的权重,代表从a1走到a2,a2走到a3,,,这样走下去的权重和,就是一个排列的权重,
然后求全排列的权重和。。

数学转换下即可:对于任意的xy,其在全排列中相邻的组合数是(n-1)!*2。

这样题目意思就明了了,求一棵树中任意两点距离和,边上有权重。

贡献思维,看每条边对答案的贡献。
一条边被n1*n2次走过,n1,n2分别为边两边的子树的节点数。

树形DP去解析,有三个状态,子节点数,子节点到根节点距离和,子树中任意两点距离和。

三个状态具体如何转移详见代码,状态和顺序绝对不能错。

(我这个好像写烦了,,其实就是简单的对每个边求其贡献即可,分开的两颗子树节点乘积*权重。。。)
*/

struct node///链式前向星板子
{
    int nxt;
    int e;
    ll w;
    node(int x=0,int y=0,ll z=0)
    {
        e=x;
        nxt=y;
        w=z;
    }
};

node edge[maxn<<1];
int head[maxn],tot=0;
void init(){memset(head,-1,sizeof(head));tot=0;}
void add(int a,int b,ll c){edge[tot]=node(b,head[a],c);head[a]=tot++;}

ll n,dp[maxn][3];///0代表子树的节点数,1代表子树中所有点到根节点的路径之和,2代表子树中任意点对距离和。
void dfs(int u,int p)
{
    if(head[u]==-1) {dp[u][0]=1;return ;}
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v=edge[i].e;
        if(v==p) continue;
        dfs(v,u);
        (dp[u][2]+=dp[v][2]+dp[v][1]*dp[u][0]%mod+dp[v][0]*dp[u][1]%mod+dp[u][0]*dp[v][0]%mod*edge[i].w%mod)%=mod;///又是一个线性的动态规划
        (dp[u][1]+=dp[v][1]%mod+dp[v][0]*edge[i].w%mod)%=mod;
        (dp[u][0]+=dp[v][0])%=mod;///子树的顶点数
    }
    dp[u][0]++;
    (dp[u][2]+=dp[u][1])%=mod;///
}

int x,y;
ll fac[maxn],z;

int main()
{
    fac[0]=1;for(int i=1;i<maxn;i++) fac[i]=fac[i-1]*i%mod;

    while(~scanf("%lld",&n))
    {
        init();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%lld",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
        }
        memset(dp,0,sizeof(dp));
        dfs(1,-1);
        ll ans=dp[1][2]*fac[n-1]%mod*2%mod;
        printf("%lld\n",ans);
    }
    return 0;
}
/*
4
1 3 1
1 2 1
1 4 1


*/
           

继续阅读