1. 程式人生 > 實用技巧 >[洛谷P4492] HAOI2018 蘋果樹

[洛谷P4492] HAOI2018 蘋果樹

問題描述

小 C 在自己家的花園裡種了一棵蘋果樹, 樹上每個結點都有恰好兩個分支. 經過細心的觀察, 小 C 發現每一天這棵樹都會生長出一個新的結點.

第一天的時候, 果樹會長出一個根結點, 以後每一天, 果樹會隨機選擇一個當前樹中沒有長出過結點 的分支, 然後在這個分支上長出一個新結點, 新結點與分支所屬的結點之間連線上一條邊.

小 C 定義一棵果樹的不便度為樹上兩兩結點之間的距離之和, 兩個結點之間 的距離定義為從一個點走到另一個點的路徑經過的邊數.

現在他非常好奇, 如果 N 天之後小 G 來他家摘蘋果, 這個不便度的期望 E 是多少. 但是小 C 討厭分數, 所以他只想知道 \(E \times N !\)

\(P\) 取模的結果, 可以證明這是一個整數.

輸入格式

從標準輸入中讀入資料. 一行兩個整數 \(N, P\) .

輸出格式

輸出到標準輸出中. 輸出一個整數表示答案.

樣例輸入

3 610745795

樣例輸出

24

資料範圍

\(N \le 2000,P\le 10^9+7\)

解析

老套路題了。

不難發現,第 N 天的樹上一定有 N+1 個分支可以長結點。因此,大小為 N 的滿足要求的樹有 N! 中方法。

直接統計十分複雜,我們考慮計算每條邊的貢獻。設當前列舉的是節點 \(i\) 的父邊,其子樹大小為 \(j\) 。那麼,在確定樹的形態的情況下,這條邊會被經過 \(j(n-j)\) 次。

接下來分別考慮子樹和子樹外的形態。對於子樹,由之前的結論,有 \(j!\) 種不同的形態。而考慮編號的問題,子樹內的編號一定都大於 \(i\) 。所以總方案數為 \(j!C_{n-i}^{j-1}\) 。對於子樹外,首先在前 \(i\) 天時就有 \(i!\) 中形態。在第 \(i\) 天之後,除了欽定的在子樹內的點以外,其餘的每一個點都可以放在子樹外的一個分支上,依次有 \(i+1-2...n-j-1\) 種方案(因為點放在子樹內導致的分支增加對子樹外沒有影響,畢竟不能放上去)。綜上,總貢獻為

\[j(n-j)\times j!C_{n-i}^{j-1}\times i(i-1)(n-j-1)! \]

列舉 \(i\)\(j\) 即可。

程式碼

#include <iostream>
#include <cstdio>
#define int long long
#define N 2002
using namespace std;
int n,mod,i,j,c[N][N],fac[N],ans;
signed main()
{
	scanf("%lld%lld",&n,&mod);
	for(i=fac[0]=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	c[0][0]=1;
	for(i=1;i<=n;i++){
		c[i][0]=1;
		for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=n-i+1;j++){
			int tmp=fac[j]*c[n-i][j-1]%mod*i%mod*(i-1)%mod*fac[n-j-1]%mod*j%mod*(n-j)%mod;
			ans=(ans+tmp)%mod;
		}
	}
	printf("%lld\n",ans);
	return 0;
}