題解 P4492 [HAOI2018]蘋果樹
阿新 • • 發佈:2021-08-05
題意簡述
第 \(i\) 次將節點 \(i\) 隨機連在樹上某個葉子的左側或右側(如果存在空位),生成一棵 \(n\) 個點的二叉樹,求二叉樹兩兩節點的距離之和期望。
\(n \leq 2000\)。
題目分析
首先,每長出一個葉子,就會減少一個連線位置,再增加兩個連線位置,也就是總共多了一個連線位置。樹的大小為 \(x\) 時,就有 \(x+1\) 個連線位置,因此總方案數為 \(1 \times 2 \times \cdots \times n=n!\)。
再來考慮“不便度”,經典套路是列舉每一條路徑考慮貢獻次數。設節點 \(u\) 的子樹大小為 \(siz_u\),則連線 \(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,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; }