P4492 [HAOI2018]蘋果樹
阿新 • • 發佈:2020-10-12
有一個顯然的套路
\(i\)的父邊對總距離和貢獻為\(siz_i(n-siz_i)\)
在序列問題中有一個非常常見的套路是取任意一個“分割點”然後分別考慮分割點左邊和右邊的情況,兩個乘起來就是我們要求的序列個數
同理我們在樹上也可以採取類似的套路,刪掉一條邊,考慮分開的兩個聯通塊的方案數,兩個乘起來就是合法樹的個數了
考慮\(i\)子樹的情況,從形態講,樹一共會有\(siz_i!\)種不同形態,從樹點編號講,一共有\(\large {siz_i-1\choose n-i}\)種編號,已經選了\(n-i\)個點,選了\(i\),再選\(siz_i-1\)
因為要從上向下生成樹,所以保證子樹點編號比\(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); }