zoukankan      html  css  js  c++  java
  • HDU-5378 概率DP

    题意:给定一棵有n个节点的树,现在要给节点附1~n的权值(各节点权值不能相同),一棵子树的领袖就是子树中权值最大的节点,问有多少种分配方案使得最后有恰好K个领袖。

    解法:这道题一看以为是树上的计数问题,想了好久的树形DP没想到,最后看题解才知道解法是概率DP(qwq)。解法还是非常巧妙的,感觉自己现在还没理解到它的精髓。

    先求出这棵树以i点位根的子树的结点个数son[i],然后就不用管这棵树了。设dp[i][j]为前i棵子树恰好有j个领袖的概率。

    因为问的是方案数,所以随机选,选择没有优劣之分每个点都是等概率的,那么我们可以写出dp方程。

    dp[i][j]=(dp[i-1][j]*(son[i]-1)/son[i] , dp[i-1][j-1]*1/son[i] ) 前面代表第i个点就是领袖后面代表第i个点不是领袖。

    注意这是个线性递推方程式,此时这里的i是线性的,与树的结构已经没有关系了。

    求出dp[n][k]之后,答案就是dp[n][k]*n!  

    代码要注意因为每次都要除以son[i]所以难免要求逆元,因为状态多达1000^2个所以不先把逆元预处理出来会TLE。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1e3+10;
    const int P=1e9+7;
    int n,k,son[N];
    int dp[N][N];
    vector<int> G[N];
    
    LL power(LL x,LL p) {
        LL ret=1;
        for (;p;p>>=1) {
            if (p&1) ret=ret*x%P;
            x=x*x%P;
        }
        return ret;
    }
    
    void dfs(int x,int fa) {
        son[x]=1;
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if (y==fa) continue;
            dfs(y,x);
            son[x]+=son[y];
        }
    }
    
    int inv[N];
    void prework() {
        for (int i=1;i<=1000;i++) inv[i]=power(i,P-2);
    }
    
    int main()
    {
        prework();
        int T,cas=0; cin>>T;
        while (T--) {
            scanf("%d%d",&n,&k);
            for (int i=1;i<=n;i++) G[i].clear();
            for (int i=1;i<n;i++) {
                int x,y; scanf("%d%d",&x,&y);
                G[x].push_back(y);
                G[y].push_back(x);
            }
            for (int i=1;i<=n;i++) son[i]=0;
            dfs(1,0);
            
            for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) dp[i][j]=0;
            dp[0][0]=1;
            for (int i=1;i<=n;i++) 
                for (int j=0;j<=min(i,k);j++) {
                    if (j<1) dp[i][j]=(LL)dp[i-1][j]*(son[i]-1)%P*inv[son[i]]%P;
                    else dp[i][j]=((LL)dp[i-1][j]*(son[i]-1)%P*inv[son[i]]%P+(LL)dp[i-1][j-1]*1*inv[son[i]]%P)%P;
                }
        
            int ans=dp[n][k];
            for (int i=1;i<=n;i++) ans=((LL)ans*i)%P;
            printf("Case #%d: %d
    ",++cas,ans);
        }
        return 0;
    }
  • 相关阅读:
    3-1
    3-2
    习题二 8
    习题二 3
    习题二 5
    习题二 4
    习题二 6
    实验三-2未完成
    实验三
    心得
  • 原文地址:https://www.cnblogs.com/clno1/p/11600144.html
Copyright © 2011-2022 走看看