hdu 4661Message Passing

这道题一直re,结果是忘了手动扩栈了,好伤~~~~~   以后要切记

题意:有n个人呈树状结构,每个人知道一个独特的消息。每次可以让一个人将他所知的所有消息告诉和他相邻的人。求所有人都知道所有消息花时花的步        数最少的所有方案数。

思路:以每个节点作为信息汇总点,先把信息都传到这个点,再由这个点扩展开去。

         首先建树,然后从根节点出发,计算每个儿子节点的儿子数量a1,a2,a3...........an,总儿子个数为sum,根据顺序的不同可得,

        共有C(sum,a1)*C(sum-a1,a2)*(sum-a1-a2,a3)*...........*(an-1+an-2,an-1)*(an,an),这么多种走法。

        与此同时,它的每棵子树里面若有分支,则它本身也有不同的走法,所以继续对子树1,子树2...........继续按以上方法计算,累乘。

        因为信息汇总后还需要扩展,求得的积需要再平方。

       接下来就以他的子树作为汇总点计算有集中走法,这时候子节点的总数可以利用上一步父亲节点求得的结果来计算。

      最终算出所有的走法总数。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#define LL long long
const int mod=1000000000+7;
const int maxn=1111111;
struct node
{
    int son;
    LL sum;
}f[maxn];
LL fact[maxn],ans;
int head[maxn],nxt[2*maxn],u[2*maxn],e,n;
bool vis[maxn];
void getfact()
{
    LL i;
    fact[0]=1;
    for(i=1;i<=maxn;i++)
        fact[i]=fact[i-1]*i%mod;
}
void init(int n)
{
    int i,j;
    e=0;
    for(i=1;i<=n;i++)head[i]=-1;
}
void addedge(int x,int y)
{
    u[e]=y;
    nxt[e]=head[x];
    head[x]=e++;
}
LL modstyle(LL s)
{
    if(s>=mod)s%=mod;
    if(s<0)s=s%mod+mod;
    return s;
}
void extEuclid(LL a,LL b,LL &x,LL &y)
{
    if(b==0){x=1;y=0;return ;}
    extEuclid(b,a%b,x,y);
    LL tmp=x;x=y;y=tmp-a/b*y;
}
node buildtree(int s)
{
    int i,j;
    vis[s]=1;
    LL x,y,sum=1;
    int son=0;
    node p;
    for(i=head[s];i!=-1;i=nxt[i])
    {
        if(vis[u[i]])continue;
        p=buildtree(u[i]);
        son+=p.son;
        sum=sum*p.sum%mod;
        extEuclid(fact[ p.son ]*fact[ son-p.son ]%mod,mod,x,y);
        x=modstyle(x);
        sum=sum*fact[ son ]%mod*x%mod;
    }
   f[s].son=son+1;
   f[s].sum=sum;
    return f[s];
}
void solve(int s,LL presum)
{
    int i,j,k,son=f[s].son;
    LL x,y,sum=f[s].sum,a,b;
    LL cursum;
    if(presum==-1){
        cursum=sum;
        ans+=sum*sum%mod;
    }
    else
    {
        a=fact[n-1-son]*fact[son]%mod;
        extEuclid(fact[n-1-(son-1)]*fact[son-1]%mod,mod,x,y);
        b=modstyle(x);
        cursum=presum*a%mod*b%mod;
        ans+=cursum*cursum%mod;
    }
    vis[s]=1;
    for(i=head[s];i!=-1;i=nxt[i])
    {
       if(vis[u[i]])continue;
       solve(u[i],cursum);
    }
}
int main()
{
    int i,j,t,k,x,y;
    getfact();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init(n);
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            addedge(x,y);
            addedge(y,x);
        }
        for(i=1;i<=n;i++)vis[i]=0;
        buildtree(1);
        for(i=1;i<=n;i++)vis[i]=0;
        ans=0;
        solve(1,-1);
        printf("%I64d\n",ans%mod);
    }
    return 0;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值