题解 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;
}