[概率dp] hdu 5378 Leader in Tree Land

题意:

给你一颗以1位根节点的树,我们定义对于每一个子树,节点权值最大的权值记为这个子树的权值,为你将1~n放到这个树里

满足最大权值仅仅有k个的组合数是多少。

思路:

我们能够知道以每一个节点为子树。且根节点权值最大的概率是多少,不是的概率是多少。

那么事实上问题就变成了 我们在n个物品里面。每一个物品拿的概率是pi不拿的概率是1-pi

问你拿k个物品的概率是多少

然后最后乘n!就好了。

中间计算运用逆元。

代码:

#include"cstdlib"
#include"cstring"
#include"cmath"
#include"cstdio"
#include"queue"
#include"algorithm"
#include"iostream"
#include"stack"
using namespace std;
#define ll __int64
#define N 123456
vector<int>edge[2234];
ll sum[1234],dp[1234][1234];
ll in[1234],mod=1000000007LL;
ll power(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
void dfs(int x,int f)
{
    int ans=1,lit=edge[x].size();
    for(int i=0; i<lit; i++)
    {
        int v=edge[x][i];
        if(v==f) continue;
        dfs(v,x);
        ans+=sum[v];
    }
    sum[x]=ans;
    return ;
}
int main()
{
    int t,cas=1;
    cin>>t;
    for(int i=1; i<=1234; i++) in[i]=power(i,mod-2); //预处理逆元
    while(t--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1; i<=n; i++) edge[i].clear();
        for(int i=1; i<n; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            edge[x].push_back(y);
            edge[y].push_back(x);
        }
        memset(sum,0,sizeof(sum));
        dfs(1,1);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<=k; j++)
            {
                if(j>0) dp[i][j]=(dp[i][j]+dp[i-1][j-1]*in[sum[i]])%mod;
                dp[i][j]=(dp[i][j]+(dp[i-1][j]*(sum[i]-1)%mod)*in[sum[i]]%mod)%mod;
            }
        }

        ll ans=1;
        for(int i=1; i<=n; i++) ans=(ans*i)%mod;
        ans*=dp[n][k];
        printf("Case #%d: %I64d
",cas++,ans%mod);
    }
    return 0;
}



原文地址:https://www.cnblogs.com/wgwyanfs/p/7275868.html