Hdu-4661 Message Passing(树形DP)

There are n people numbered from 1 to n. Each people have a unique message. Some pairs of people can send messages directly to each other, and this relationship forms a structure of a tree. In one turn, exactly one person sends all messages s/he currently has to another person. What is the minimum number of turns needed so that everyone has all the messages? 
This is not your task. Your task is: count the number of ways that minimizes the number of turns. Two ways are different if there exists some k such that in the k-th turn, the sender or receiver is different in the two ways. 
Input
First line, number of test cases, T. 
Following are T test cases. 
For each test case, the first line is number of people, n. Following are n-1 lines. Each line contains two numbers. 

Sum of all n <= 1000000.
Output
T lines, each line is answer to the corresponding test case. Since the answers may be very large, you should output them modulo 10  9+7.
Sample Input
2
2
1 2
3
1 2
2 3
Sample Output
2

6

题意:n个人构成一棵树,每个人有一个独特的信息,每轮最多有一个人把他携带的所有信息传递给和他相邻的人,假如最多传k轮所有人都能得到所有信息,问方案数有多少.

分析:考虑树上的每条边,可以看作有两个方向,那么每条边最多传入一次传出一次,所以最少传递次数的上限为2*(n-1),然后我们可以轻易的构造出来这种方案,只要选一个点为根每次都先通过n-1把所有的信息都传入给他,再通过n-1次将这个信息传回所有点就可以了,然后我们考虑如何统计方案,考虑每个点都作为一次汇点,假如传入a的方案数为f(a),那么我们可以根据一一对应的法则证明从a传出去的方案数也为f(a),所以最后的答案就是sigma(f(a)^2),f(a)可以用树形dp求出来,具体就是先随便找一个点作为根,然后dfs这棵树并且求出来每个点的子树符合拓扑序的所有方案数,然后再dfs一次求出以每个点为汇点的方案数.

#include <bits/stdc++.h>
#define INF 1000111111
#define N 1000005
#define MOD 1000000007
using namespace std;
typedef long long ll;
int T,n,x,y;
ll ans,gc[N],igc[N],Ans[N],Down[N],Size[N];
vector<int> G[N];
void exgcd(ll a,ll b,ll &g,ll &x,ll &y)
{
    if(!b) g=a,x=1,y=0;
    else
    {
        exgcd(b,a%b,g,y,x);
        y-=a/b*x;
    }
}
ll inv(ll a,ll n)
{
    ll d,x,y;
    exgcd(a,n,d,x,y);
    return d == 1 ? (x+n)%n : -1;
}
ll c(int x,int y)
{
    return (gc[x]*igc[y] % MOD)*igc[x-y] % MOD;
}
void dfs1(int u,int fa)
{
    Size[u] = 1,Down[u] = 1;
    for(int v : G[u])
     if(v != fa)
     {
         dfs1(v,u);
         Size[u] += Size[v];
         if(!Down[u]) Down[u] = Down[v];
         else Down[u] = (Down[u]*Down[v] % MOD)*c(Size[u] - 1,Size[v]) % MOD;
     }
}
void dfs2(int u,int fa)
{
    if(u == 1) Ans[u] = Down[u];
    else
    {
        ll res = (Ans[fa]*inv(Down[u],MOD) % MOD)*inv(c(n-1,Size[u]),MOD) % MOD;
        Ans[u] = (c(n-1,Size[u]-1)*Down[u] % MOD)*res % MOD;
    }
    ans = (ans + Ans[u]*Ans[u] % MOD) % MOD;
    for(int v : G[u])
     if(v != fa) dfs2(v,u);
}
int main()
{
    gc[0] = igc[0] = 1;
    for(int i = 1;i < N;i++) gc[i] = (gc[i-1]*i) % MOD;
    for(int i = 1;i < N;i++) igc[i] = inv(gc[i],MOD);
    scanf("%d",&T);
    while(T--)
    {
        ans = 0;
        scanf("%d",&n);
        for(int i = 1;i <= n;i++) G[i].clear();
        for(int i = 1;i < n;i++)
        {
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs1(1,1);
        dfs2(1,1);
        cout<<ans<<endl;
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值