题解 P4492 [HAOI2018]苹果树

题目传送门

题意简述

\(i\) 次将节点 \(i\) 随机连在树上某个叶子的左侧或右侧(如果存在空位),生成一棵 \(n\) 个点的二叉树,求二叉树两两节点的距离之和期望。

\(n \leq 2000\)

题目分析

首先,每长出一个叶子,就会减少一个连接位置,再增加两个连接位置,也就是总共多了一个连接位置。树的大小为 \(x\) 时,就有 \(x+1\) 个连接位置,因此总方案数为 \(1 \times 2 \times \cdots \times n=n!\)

再来考虑“不便度”,经典套路是枚举每一条路径考虑贡献次数。设节点 \(u\) 的子树大小为 \(siz_u\),则连接 \(u\)\(u\) 的父节点的边贡献了 \(siz_u(n-siz_u)\) 次,答案即为 \(\sum\limits_{i=1}^n siz_u(n-siz_u)\)

所以只需要对 \(\forall k \in [1,n]\),求出所有情况下子树大小为 \(k\) 的点的数量即可。

考虑动态规划,设 \(f_{i,k}\) 表示树的大小为 \(i\) 时,子树大小为 \(k\) 的点的数量。

考虑转移,刚才已经解释过,子树大小为 \(k\) 的点下面就会有 \(k+1\) 个连接位置。

所以在所有情况下,它有 \(k+1\) 种情况转移到 \(f_{i+1,k+1}\),有 \((n+1)-(k+1)=n-k\) 种情况转移到 \(f_{i+1,k}\)

再考虑新加的节点,列成被动转移:

\[f_{i,k}=(i-k-1)f_{i-1,k}+kf_{i-1,k-1}+[k=1]i! \]

时间复杂度 \(O(n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;
const long long N=2e3+5;
long long n,mod,ans;
long long mul[N],f[N][N];
int main(){
	cin>>n>>mod;
	mul[0]=1;
	for(long long i=1;i<=n;i++) mul[i]=mul[i-1]*i%mod;
	for(long long i=1;i<=n;i++)
		for(long long j=1;j<=i;j++)
			f[i][j]=(f[i-1][j]*(i-j-1)%mod+f[i-1][j-1]*j%mod+(j==1)*mul[i])%mod;
	for(long long i=1;i<=n;i++) ans=(ans+f[n][i]*i%mod*(n-i)%mod)%mod;
	cout<<ans;
}
posted @ 2021-08-05 15:37  苹果蓝17  阅读(64)  评论(0编辑  收藏  举报