卧薪尝胆,厚积薄发。
HAOI2018 苹果树
Date: Tue Apr 02 19:18:31 CST 2019
In Category:
NoCategory
Description:
一棵二叉树,刚开始有一个点,然后每次在所有可以插入一个新点的地方等概率选一个插入,求最后两两节点之间的距离之和的期望乘
$N!$
的结果。
$1\leqslant n\leqslant 2000$
Solution:
刚开始觉得是一步一步
$DP$
发现不可做。
首先加入第
$i$
个点的时候有
$i$
种选法,也就是说最后有
$N!$
种不同的二叉树,换个角度理解可以看成是每一种中序遍历都对应同一种树,直接计算不好算,我们可以对于每一条边算贡献,对于
$(i,fa[i])$
这条边,贡献显然是
$siz[i]\times (n-siz[i])$
,那么我们可以先枚举
$i$
,然后再枚举
$siz[i]$
,于是现在我们的问题变成了求有多少种树
$i$
的子树有
$siz[i]$
个点,里面的是
$siz[i]!\binom{n-i}{siz[i]-1}$
,外面的在
$i$
之前有
$i!$
种方案,在
$i$
的后面除了确定的其他的都不能放在
$i$
的子树中,方案数就是
$(i-1)i(i+1)\cdots (n-siz[i]-1)$
最后就是
$i(i-1)(n-siz[i]-1)$
,于是答案就是:
$$
ans=\sum_{i=2}^n\sum_{siz=1}^{n-i+1}siz!\binom{n-i}{siz-1}i(i-1)(n-siz-1)!siz(n-siz)
$$
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
inline int rd()
{
register int res = 0,f = 1;register char c = getchar();
while(!isdigit(c)){if(c == '-')f = -1;c = getchar();}
while(isdigit(c))res = (res << 1) + (res << 3) + c - '0',c = getchar();
return res * f;
}
int n,p;
#define MAXN 2010
int C[MAXN][MAXN];
int Add(int a,int b){return (a + b >= p ? a + b - p : a + b);}
int fac[MAXN];
int main()
{
scanf("%d%d",&n,&p);
C[0][0] = 1;
fac[0] = 1;
for(int i = 1;i <= n;++i)
{
fac[i] = 1ll * fac[i - 1] * i % p;
C[i][0] = 1;
for(int j = 1;j <= i;++j)C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % p;
}
int ans = 0;
for(int i = 2;i <= n;++i)
{
for(int siz = 1;siz <= n - i + 1;++siz)
{
ans = Add(ans,1ll * fac[siz] * C[n - i][siz - 1] % p * i % p * (i - 1) % p * fac[n - siz - 1] % p * siz % p * (n - siz) % p);
}
}
cout << ans << endl;
return 0;
}
In tag:
DP-计数DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡