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

P4492 [HAOI2018]蘋果樹

有一個顯然的套路

\(i\)的父邊對總距離和貢獻為\(siz_i(n-siz_i)\)

在序列問題中有一個非常常見的套路是取任意一個“分割點”然後分別考慮分割點左邊和右邊的情況,兩個乘起來就是我們要求的序列個數

同理我們在樹上也可以採取類似的套路,刪掉一條邊,考慮分開的兩個聯通塊的方案數,兩個乘起來就是合法樹的個數了

考慮\(i\)子樹的情況,從形態講,樹一共會有\(siz_i!\)種不同形態,從樹點編號講,一共有\(\large {siz_i-1\choose n-i}\)種編號,已經選了\(n-i\)個點,選了\(i\),再選\(siz_i-1\)

因為要從上向下生成樹,所以保證子樹點編號比\(i\)

大來滿足,所以子樹方案數\(\large siz_i!{siz_i-1\choose n-i}\)

考慮子樹外的情況

生成\(i\)之前共\(i!\)種不同方式,生成\(i\)後是不可將點放在\(i\)子樹中,後面的點有\((i+1-2),(i+2-2),(i+3-2)...(n-siz_i+1-2)\)的生成方式

化簡\(i(i-1)(n-siz_i-1)!\)

子樹外的方案\(\large siz_i!{siz_i-1\choose n-i}i(i-1)(n-siz_i-1)!\)

總方案

\[\sum_{i=2}^n\sum_{siz=1}^{n-i+1}siz_i!{siz_i-1\choose n-i}i(i-1)(n-siz_i-1)! \]

const int N = 2005;
ll mod,dp[N][N],fac[N],c[N][N],res;int n;
int main(){
	scanf("%d%lld",&n,&mod);fac[0] = 1; dp[1][1] = 1;
	for(int i = 0;i <= n;++i) c[i][i] = c[0][i] = 1;
	for(int i = 0;i <= n;++i)
		for(int j = 1;j < n;++j)
			c[j][i] = (c[j-1][i-1] + c[j][i-1]) % mod;
	for(int i = 1;i <= n;++i) fac[i] = fac[i-1] * i % mod;
	for(int i = 2;i <= n;++i)
		for(int j = 1;j <= i;++j)
			dp[i][j] = fac[i-2] * j % mod * (j-1) % mod;
	for(int i = 2;i <= n;++i)
        for(int j = 1;j <= n-i+1;++j)
            (res += fac[j]*c[j-1][n-i]%mod*j*(n-j)%mod*dp[n-j+1][i]) %= mod;
    printf("%lld",res);
}