题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5378
题意:给你一棵n个结点的有根树。因为是有根树,那么每个结点可以指定以它为根的子树(后面讨论的子树都是这个)。现在我们把标号从1到n的n个minister派驻到这些结点上面(每个结点派驻一人),并规定任一子树中编号最大的minister 为该子树的领导,问你存在多少个不同的领导
解:
引用官方题解:
可以用求概率的思想来解决这个问题。令以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!乘上概率就是答案。计算的时候用逆元代替上面的分数即可
1 /* 2 * Problem: hdu5378 Leader in Tree Land 3 * Author: SHJWUDP 4 * Created Time: 2015/8/12 星期三 20:17:31 5 * File Name: 1006.cpp 6 * State: Accepted 7 * Memo: 概率dp,乘法逆元 8 */ 9 #include <iostream> 10 #include <cstdio> 11 #include <vector> 12 #include <cstring> 13 #include <algorithm> 14 15 using namespace std; 16 17 const int MOD=1e9+7; 18 19 struct Edge { 20 int u, v; 21 }; 22 23 int n, k; 24 vector<Edge> edges; 25 vector<vector<int> > G; 26 vector<int> siz, inv; ///siz[i]*inv[i]=1(mod 1e9+7) 27 void init() { 28 edges.clear(); 29 G.assign(n+1, vector<int>(0)); 30 siz.resize(n+1); 31 inv.resize(n+1); 32 } 33 void addEdge(int u, int v) { 34 edges.push_back((Edge){u, v}); 35 G[u].push_back(edges.size()-1); 36 } 37 int pow_mod(long long x, int n, int mod) { 38 long long res=1; 39 while(n) { 40 if(n & 1) res=res*x%mod; 41 x=x*x%mod; 42 n>>=1; 43 } 44 return res; 45 } 46 void dfs(int u, int fa) { 47 siz[u]=1; 48 for(int i : G[u]) { 49 Edge & e=edges[i]; 50 if(e.v==fa) continue; 51 dfs(e.v, u); 52 siz[u]+=siz[e.v]; 53 } 54 } 55 int main() { 56 #ifndef ONLINE_JUDGE 57 freopen("in", "r", stdin); 58 //freopen("out", "w", stdout); 59 #endif 60 int T, now=0; 61 scanf("%d", &T); 62 while(T--) { 63 scanf("%d%d", &n, &k); 64 init(); 65 for(int i=0; i<n-1; i++) { 66 int a, b; 67 scanf("%d%d", &a, &b); 68 addEdge(a, b); 69 addEdge(b, a); 70 } 71 dfs(1, -1); 72 for(int i=1; i<=n; i++) inv[i]=pow_mod(siz[i], MOD-2, MOD); //求siz[i]的乘法逆元 73 vector<vector<long long> > f(n+1, vector<long long>(n+1, 0)); 74 f[0][0]=1; 75 for(int i=1; i<=n; i++) { 76 for(int j=0; j<=k; j++) { 77 f[i][j]+=f[i-1][j]*(siz[i]-1)%MOD*inv[i]%MOD; 78 if(j>0)f[i][j]+=f[i-1][j-1]*inv[i]%MOD; 79 f[i][j]%=MOD; 80 } 81 } 82 long long ans=f[n][k]; 83 for(int i=1; i<=n; i++) { 84 ans=(ans*i)%MOD; 85 } 86 printf("Case #%d: ", ++now); 87 printf("%I64d\n", ans); 88 } 89 return 0; 90 }
时间: 2024-10-06 20:19:54