zoukankan      html  css  js  c++  java
  • 【HDU5378】Leader in Tree Land-概率DP+逆元+好题

    测试地址:Leader in Tree Land
    题目大意:给定一棵有n个节点的以1号节点为根的有根树,现在要给节点附上1~n的权值(各节点权值不能相同),一棵子树的领袖就是子树中权值最大的节点,问有多少种分配方案使得最后有恰好K个领袖。
    做法:本题是一道概率DP的题目。
    这道题目从表面上看,怎么看都是树形DP啊,组合数学啊一堆乱糟糟的东西组合在一起的狂暴计数题,深入分析后也会发现树形DP的状态转移方程非常难找,因为想不到什么方法合并多个子树的方案数。其实,这道题是一道隐藏很深的概率DP,并且甚至都不用在树上做。
    我们知道,总的赋值方案共有n!个,那么我们只需要求出使得有K个领袖的方案出现的概率,就可以算出方案数了。关于这一点,我们发现,每一个节点作为以它自己为根的子树的领袖的概率是相互独立的,为1/siz(i),其中siz(i)指以节点i为根的子树的节点个数。那么,我们可以设dp(i,j)为以节点1 i为根的子树中,有j个是领袖的概率,显然有状态转移方程:
    dp(i,j)=dp(i1,j)×(siz(i)1)/siz(i)+dp(i1,j1)×1/siz(i)
    边界条件是dp(0,0)=1,最后的答案为dp(n,K)×n!。那么我们只需要O(n)预处理出siz(i),然后O(n2)算出dp(n,K)即可完成此题。要注意的是,最后答案要模一个大质数,所以将以上的除以分母都改成乘以分母的逆元即可。
    综合来说,本题的题面具有强大的迷惑性,第一是这个题目出在树的模型上,就很容易把选手的思路导向树算法上,第二就是这个题目是一个计数的题目,一般很难想到用总数乘以概率算方案数,所以这个题目还是很巧妙的,学习了。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    #define mod 1000000007
    using namespace std;
    int T,n,k,first[1010],tot;
    ll inv[1010],siz[1010],dp[1010][1010];
    struct edge {int v,next;} e[2010];
    
    void insert(int a,int b)
    {
        e[++tot].v=b;
        e[tot].next=first[a];
        first[a]=tot;
    }
    
    ll power(ll a,ll b)
    {
        ll ans=1,s=a;
        while(b)
        {
            if (b&1) ans=(ans*s)%mod;
            b>>=1;s=(s*s)%mod;
        }
        return ans;
    }
    
    void dfs(int v,int f)
    {
        siz[v]=1;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=f)
            {
                dfs(e[i].v,v);
                siz[v]+=siz[e[i].v];
            }
    }
    
    int main()
    {
        scanf("%d",&T);
        for(int i=1;i<=1000;i++)
            inv[i]=power((ll)i,mod-2);
        for(int t=1;t<=T;t++)
        {
            memset(first,0,sizeof(first));
            tot=0;
            scanf("%d%d",&n,&k);
            for(int i=1;i<n;i++)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                insert(a,b),insert(b,a);
            }
    
            dfs(1,0);
            memset(dp,0,sizeof(dp));
            dp[0][0]=1;
            for(int i=1;i<=n;i++)
            {
                dp[i][0]=(((dp[i-1][0]*(siz[i]-1))%mod)*inv[siz[i]])%mod;
                for(int j=1;j<=i;j++)
                {
                    dp[i][j]=(((dp[i-1][j]*(siz[i]-1))%mod)*inv[siz[i]])%mod;
                    dp[i][j]=(dp[i][j]+dp[i-1][j-1]*inv[siz[i]])%mod;
                }
            }
            for(int i=1;i<=n;i++) dp[n][k]=(dp[n][k]*i)%mod;
            printf("Case #%d: %lld
    ",t,dp[n][k]); 
        }
    
        return 0;
    }
  • 相关阅读:
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十一)
    install ubuntu on Android mobile phone
    Mac OS, Mac OSX 与Darwin
    About darwin OS
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十)
    Linux下编译安装qemu和libvirt
    libvirt(virsh命令总结)
    深入浅出 kvm qemu libvirt
    自然语言交流系统 phxnet团队 创新实训 项目博客 (九)
    自然语言交流系统 phxnet团队 创新实训 项目博客 (八)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793578.html
Copyright © 2011-2022 走看看