标签:概率
可以用求概率的思想来解决这个问题。令以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
#include <bits/stdc++.h>
using namespace std;
#define prt(k) cerr<<#k" = "<<k<<endl
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll mod = 1e9+7;
const ll N = 1005;
int sz[N];
vector<int> g[N];
ll dp[N][N];
int n, K;
ll fac[N];
ll pmod(ll a, ll n)
{
ll r = 1;
for (; n>0; n>>=1, a=a*a%mod) {
if (n & 1) r = r * a % mod;
}
return r;
}
inline ll inv(ll a)
{
return pmod(a, mod - 2);
}
void dfs(int u, int fa)
{
sz[u] = 1;
for (int v: g[u]) if (v-fa) {
dfs(v, u);
sz[u] += sz[v];
}
}
ll in[N];
int main()
{
fac[0] = 1; in[1] = 1;
for (ll i=1;i<N;i++) fac[i]=fac[i-1]*i%mod, in[i]=inv(i);
int re; scanf("%d", &re); int ca= 1;
while (re--) {
memset(sz,0 , sizeof sz);
scanf("%d%d", &n ,&K);
for(ll i=0;i<=n;i++) g[i].clear();
memset(dp, 0, sizeof dp);
for (ll i=0;i<n-1;i++) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 1);
dp[0][0] = 1;
for (int i=1;i<=n;i++)
for (int j=0;j<=K;j++) {
dp[i][j] = dp[i-1][j] * (sz[i] - 1) %mod
* in[sz[i]] % mod;
if (j > 0)
dp[i][j] += dp[i-1][j-1] * in[sz[i]] % mod;
dp[i][j] %= mod;
}
ll ans = dp[n][K] * fac[n] % mod;
printf("Case #%d: %I64d\n", ca++, ans);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:概率
原文地址:http://blog.csdn.net/oilover/article/details/47426985