[HAOI2018]苹果树
[HAOI2018]苹果树
题目大意:
一个\(n(n\le2000)\)个点的二叉树,初始时只有一个根结点\(1\)。按顺序加入\(2\sim n\)的结点。每次随机一个未拓展的分支加入一个新的结点。求最后树上所有点对之间的距离之和的期望\(E\times n!\)对\(p(p\le10^9+7)\)取模的结果。
思路:
对于\(n\)个点的二叉树,加入点\(1\)时有\(1\)个分支可以加,加入点\(2\)时有\(2\)个分支可以加,以此类推,加入点\(n\)时有\(n\)个分支可以加。每加入一个点会在覆盖掉原来一个分支的基础上增加两个分支。因此若考虑结点编号的不同,\(n\)个结点的二叉树有\(n!\)种形态。
将问题转化为加入一条边后,每条边对答案的贡献。若树的形态确定,考虑经过一条边的点对,若在树上去掉这条边,将树分为大小分别为\(j\)和\(n-j\)的两个连通块,则这条边对答案有\(n(n-j)\)的贡献。
然而,现在树的形态是不确定的。对于现在加入第\(i-1\)条边,我们不妨枚举边下端的子树大小\(j\)。显然若在子树内结点编号确定的情况下,共有\(j!\)种子树。子树的根结点一定是\(i\),从\(n-i\)个结点中选择\(j-1\)个作为子树中的结点,总共有\(\binom{n-i}{j-1}\)种方法。
对于编号为\(1\sim i\)的点,共有\(i!\)种生成方式。对于剩下的\(n-j-i+1\)个结点,由于不能让它们加到\(i\)的子树中,因此他们分别有\((i-1),i,\ldots,(n-j-1)\)种方法可以加。也就是说,算上前面\(i!\)个点,子树外共有\((n-j-1)!\cdot i\cdot(i-1)\)种方法。
答案即为\(\sum_{i=2}^n\sum_{j=1}^{n-i+1}j!\cdot\binom{n-i}{j-1}\cdot j\cdot(n-j)!\cdot i\cdot(i-1)\)。
时间复杂度\(\mathcal O(n^2)\)。
源代码:
#include<cstdio>
#include<cctype>
typedef long long int64;
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=2001;
int n,mod,fac[N],c[N][N];
int main() {
n=getint(),mod=getint();
for(register int i=c[0][0]=1;i<=n;i++) {
for(register int j=c[i][0]=1;j<=i;j++) {
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
for(register int i=fac[0]=1;i<=n;i++) {
fac[i]=(int64)fac[i-1]*i%mod;
}
int ans=0;
for(register int i=2;i<=n;i++) {
for(register int j=1;j<=n-i+1;j++) {
(ans+=(int64)j*fac[j]%mod*c[n-i][j-1]%mod*fac[n-j]%mod*i*(i-1)%mod)%=mod;
}
}
printf("%d\n",ans);
return 0;
}