2015多校第7场 HDU 5378 Leader in Tree Land 概率DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5378
题意:一棵n个节点的树。对其节点进行标号(1~n)。求恰好存在k个节点的标号是其节点所在子树的最大值的方案数。
解法:
首先,总共有n!中标号方案。而如果求出n个节点中出现k个上述节点的概率p。方案数等于n!* p。
dp[i][j] 表示钱i个节点有j个上述节点的概率。转移较容易推出。
dp[i][j] = dp[i-1][j] * (c[i]-1) / c[i] + dp[i-1][j-1] * (1 / c[i]); c[i] 第i个节点的子树的节点数。
有除法要处理逆元。
//HDU 5378 #include <bits/stdc++.h> using namespace std; typedef long long LL; const LL mod = 1e9+7; const int maxn = 1010; int n, k, edgecnt, head[maxn]; struct edge{ int to,next; }E[maxn*2]; void init(){ edgecnt=0; memset(head,-1,sizeof(head)); } void add(int u, int v){ E[edgecnt].to=v,E[edgecnt].next=head[u],head[u]=edgecnt++; } int sz[maxn]; LL inv[maxn], dp[maxn][maxn]; //dp[i][j]代表前i个点,恰好有j个点是leader的概率 //dp[i][j] = dp[i-1][j]*(sz[i]-1)/sz[i]+dp[i-1][j-1]/sz[i] void dfs(int u, int pre){ sz[u] = 1; for(int i = head[u]; ~i; i=E[i].next){ int v = E[i].to; if(v == pre) continue; dfs(v, u); sz[u] += sz[v]; } } void INIT(){ inv[1] = 1; for(int i = 2; i < maxn; i++) inv[i] = 1LL*(mod-mod/i)*inv[mod%i]%mod; } int main() { init(); int T, ks = 0; INIT(); scanf("%d", &T); while(T--) { init(); scanf("%d %d", &n,&k); for(int i=1; i<n; i++){ int u, v; scanf("%d %d", &u,&v); add(u, v); add(v, u); } dfs(1, -1); memset(dp, 0, sizeof(dp)); dp[0][0] = 1; dp[1][0] = (sz[1]-1)*inv[sz[1]]%mod; dp[1][1] = inv[sz[1]]; for(int i=2; i<=n; i++){ dp[i][0] = dp[i-1][0]*(sz[i]-1)%mod*inv[sz[i]]%mod; for(int j=1; j<=min(i,k); j++){ dp[i][j] = ((dp[i-1][j]*(sz[i]-1))%mod*inv[sz[i]]%mod)%mod; dp[i][j] = (dp[i][j] + (dp[i-1][j-1]%mod*inv[sz[i]]%mod)%mod)%mod; } } LL ans = dp[n][k]; for(int i=1; i<=n; i++){ ans = (ans*i)%mod; } printf("Case #%d: %lld\n", ++ks, ans%mod); } return 0; }