[概率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\n",cas++,ans%mod); } return 0; }