1. 程式人生 > 其它 >題解 P4492 [HAOI2018]蘋果樹

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