方法太屌,只能看一看了.....
可以用求概率的思想来解决这个问题。令以i号节点为根的子树为第i棵子树,设这颗子树恰好有sz[i]个点。那么第i个点是第i棵子树最大值的概率为1/sz[i],不是最大值的概率为(sz[i]-1)/sz[i]。现在可以求解恰好有k个最大值的概率。
令dp[i][j]表示考虑编号从1到i的点,其中恰好有j个点是其子树最大值的概率。 很容易得到如下转移方程:dp[i][j]=dp[i-1][j]*(sz[i]-1)/sz[i]+dp[i-1][j-1]/sz[i]。这样dp[n][k]就是所有点中恰好有k个最大值的概率。
题目要求的是方案数,用总数n!乘上概率就是答案。计算的时候用逆元代替上面的分数即可。
2 3 2 1 2 1 3 10 8 2 1 3 2 4 1 5 3 6 1 7 3 8 7 9 7 10 6
Case #1: 4 Case #2: 316512
/* *********************************************** Author :CKboss Created Time :2015年08月13日 星期四 10时59分40秒 File Name :HDOJ5378.cpp ************************************************ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <string> #include <cmath> #include <cstdlib> #include <vector> #include <queue> #include <set> #include <map> using namespace std; typedef long long int LL; const int maxn=1100; const LL mod=1000000007LL; int n,k; LL dp[maxn][maxn],sz[maxn]; vector<int> G[maxn]; void clear() { for(int i=0;i<=n+10;i++) G[i].clear(); } LL dfs(int u,int fa) { sz[u]=1LL; for(int i=0,ss=G[u].size();i<ss;i++) { int v=G[u][i]; if(v==fa) continue; sz[u]+=dfs(v,u); } return sz[u]; } LL inv[2*maxn],jc[2*maxn]; void init() { jc[0]=1; inv[1]=1;jc[1]=1; for(LL i=2;i<2*maxn;i++) { inv[i]=(inv[mod%i]*(mod-mod/i))%mod; jc[i]=(jc[i-1]*i)%mod; } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); init(); int T_T,cas=1; scanf("%d",&T_T); while(T_T--) { scanf("%d%d",&n,&k); clear(); for(int i=0,u,v;i<n-1;i++) { scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs(1,1); dp[0][0]=1LL; for(int i=1;i<=n;i++) { for(int j=0;j<=i;j++) { dp[i][j]=((dp[i-1][j]*(sz[i]-1))%mod*inv[sz[i]])%mod; if(j) dp[i][j]=(dp[i][j]+(dp[i-1][j-1]*inv[sz[i]])%mod)%mod; } } LL ans=(dp[n][k]*jc[n])%mod; cout<<"Case #"<<cas++<<": "<<ans<<endl; } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDOJ 5378 Leader in Tree Land 概率DP
原文:http://blog.csdn.net/ck_boss/article/details/47622857