hdu 4661 Message Passing(树形DP&组合数学)

Message Passing

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 1187    Accepted Submission(s): 423

Problem Description

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 109+7.

Sample Input

2
2
1 2
3
1 2
2 3

Sample Output

2
6

Source

2013 Multi-University Training Contest 6

Recommend

zhuyuanchen520   |   We have carefully selected several similar problems for you:  5017 5016 5015 5014 5013

题意:

n个人,构成树形关系。每个人有一条独一无二的信息,每个人可以将自己的信息通过树边,共享给与他相邻的人,共享之后,被共享的人拥有他原有的信息和共享的来的信息。每次共享为一次操作,问每个人都拥有所有人的信息最小要的次数的共享方法有多少种。

思路:

容易的出,最短的时间内,当然是每个节点将自己的信息想外传出去一次,并且接受一次信息,也就是树边的2倍2*(n-1)。

然后可以证明,在最短时间内,所有的传递方式都有一个“信息转换点”——其他节点的信息首先传递到此节点,然后信息再从这个节点向其他节点传递。其实我们要求的就是拓扑序有多少种。定义dp[u]表示u节点以下,传到u节点的拓扑序有多少种,cnt[u]表示u有多少个子孙节点,f[i] = i!(i的阶乘),c[i][j]表示组合数。假设它有v1,v2,v3个节点,它们的拓扑序分别有dp[v1],dp[v2],dp[v3]这么多种。那么dp[u] = c[cnt[u]-1][cnt[v1]] * c[cnt[u]-1-cnt[v1]][cnt[v2]]
* c[cnt[u]-1-cnt[v1]-cnt[v2]][cnt[v3]] * dp[v1] * dp[v2] * dp[v3](这个自己推推吧)。化简以后,得到dp[u] = f[cnt[u]-1] / ( f[cnt[v1]] * f[cnt[v2]] * f[cnt[v3]] ) * dp[v1] * dp[v2] * dp[v3] 。我们可以在o(n)的时间复杂度内算出以1节点为根的所有dp值(那么以1为根的答案就算出来了),以及其他一些辅助信息的值。然后按树的结构往下遍历,分别计算以其他节点为根的答案。以上是网上的思路。我想说的是自己的一点理解。为什么知道每个子树的拓扑序数目。就可以退出自己的拓扑序数目呢。其实很好理解的。当每个子树的拓扑序定下来之后。确定总顺序的时候。也就是要得到一个长度为cnt[u]拓扑序列。对于子树i。也有一个长度为cnt[i]拓扑序列,所以就要在cnt[u]里找cnt[i]个位置。其它子树再在剩下的子树里找。还有换根的时候该怎么推导。先写出

dp[u]‘=dp[u]*(n-sz[v]-1)!*sz[v]!/((n-1)!*dp[v])

dp[v]‘=dp[v]*(n-1)!*dp[u]‘/((sz[v]-1)!*(n-sz[v])!)。

带入dp[u]‘就可以约掉很多东西了。所以推公式的时候不要急着得到最后答案。还有就是为什么答案数就是拓扑序数的平方。因为信息传回去的时候就是你拓扑序嘛。和拓扑序数目一样的。每一个正拓扑序可以和一个逆拓扑序组合。所以就有平方种啦。

详细见代码:

#include<algorithm>
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1000010;
#pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long ll;
const ll mod=1e9+7;
ll fac[maxn],dp[maxn],ans;
int cnt,sz[maxn],n;
struct node
{
    int v;
    node *next;
} ed[maxn<<1],*head[maxn];
void adde(int u,int v)
{
    ed[cnt].v=v;
    ed[cnt].next=head[u];
    head[u]=&ed[cnt++];
}
ll pow_mod(ll x,int k)
{
    ll base=x,ret=1;
    while(k)
    {
        if(k&1)
            ret=(ret*base)%mod;
        base=(base*base)%mod;
        k>>=1;
    }
    return ret;
}
ll ni(ll x){    return pow_mod(x,mod-2);    }
void dfs(int fa,int u)
{
    ll tp;
    dp[u]=tp=sz[u]=1;
    for(node *p=head[u];p!=NULL;p=p->next)
    {
        int v=p->v;
        if(v==fa)
            continue;
        dfs(u,v);
        tp=(tp*ni(fac[sz[v]]))%mod;
        dp[u]=(dp[u]*dp[v])%mod;
        sz[u]+=sz[v];
    }
    dp[u]=(dp[u]*fac[sz[u]-1]%mod*tp)%mod;
}
void solve(int fa,int u,ll tp)
{
    ll tt;
    ans=(ans+tp*tp%mod)%mod;
    for(node *p=head[u];p!=NULL;p=p->next)
    {
        int v=p->v;
        if(v==fa)
            continue;
        tt=(tp*fac[n-sz[v]-1]%mod*fac[sz[v]])%mod;
        tt=(tt*ni(fac[sz[v]-1])%mod*ni(fac[n-sz[v]]))%mod;
        solve(u,v,tt);
    }
}
int main()
{
    int t,i,u,v,rt;

    fac[0]=fac[1]=1;
    for(i=2;i<maxn;i++)
        fac[i]=(i*fac[i-1])%mod;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        cnt=0,ans=0;
        for(i=1;i<=n;i++)
            head[i]=NULL;
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            adde(u,v);
            adde(v,u);
        }
        rt=(n+1)/2;
        dfs(-1,rt);
        solve(-1,rt,dp[rt]);
        printf("%I64d\n",ans);
    }
    return 0;
}
时间: 2024-10-12 11:37:53

hdu 4661 Message Passing(树形DP&组合数学)的相关文章

hdu 4661 Message Passing (思维 dp求拓扑排序数)

Message Passing Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 1184    Accepted Submission(s): 420 Problem Description There are n people numbered from 1 to n. Each people have a unique mes

HDU 1011 Starship Troopers(树形DP)

Starship Troopers Time Limit : 10000/5000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submission(s) : 62   Accepted Submission(s) : 12 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description You, the leader of

hdu 1520Anniversary party(简单树形dp)

Anniversary party Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4310    Accepted Submission(s): 1976 Problem Description There is going to be a party to celebrate the 80-th Anniversary of the

hdu 4044 GeoDefense (树形dp+01背包)

GeoDefense Time Limit: 12000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 663    Accepted Submission(s): 267 Problem Description Tower defense is a kind of real-time strategy computer games. The goal of towe

HDU 2196 Computer 经典树形DP

一开始看错题了,后来发现原来是在一颗带权的树上面求出距离每一个点的最长距离,做两次dfs就好,具体的看注释? #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #incl

hdu 5452 Minimum Cut 树形dp

Minimum Cut Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5452 Description Given a simple unweighted graph G (an undirected graph containing no loops nor multiple edges) with n nodes and m edges. Let T be a spa

HDU 2376 Average distance (树形dp)

Average distance Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 588    Accepted Submission(s): 213 Special Judge Problem Description Given a tree, calculate the average distance between two ve

POJ 2342 &amp;&amp;HDU 1520 Anniversary party 树形DP 水题

一个公司的职员是分级制度的,所有员工刚好是一个树形结构,现在公司要举办一个聚会,邀请部分职员来参加. 要求: 1.为了聚会有趣,若邀请了一个职员,则该职员的直接上级(即父节点)和直接下级(即儿子节点)都不能被邀请 2.每一个员工都有一个兴奋值,在满足1的条件下,要使得邀请来的员工的兴奋值最高 输出最高的兴奋值. 简单的树形DP dp[i][1]:表示以i为根的子树,邀请节点i的最大兴奋值 dp[i][0]:表示以i为根的子树,不邀请节点i的最大兴奋值 先根据入度找出整棵树的根节点, 然后一次DF

HDU 4714:Tree2cycle 树形DP

Tree2cycle 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4714 题意: 给出一棵树,可以删边和加边将这棵树变成一个圈(没有任何分支),求最少的操作次数. 题解: 将树改造为圈可以通过现将树变成一条没有分叉的链后再加上一条边连接链的两端. 再简化一下就变成了选一条路径作为主干,再将其他所有分叉通过增删边连接到竹竿上的过程. 设DP1[i](把以 i 为根节点的子树作为主干的一部分),和DP2[i](把以 i 为根节点的子树作为分叉),