[HAOI2018]苹果树(组合数学,计数)

[HAOI2018]苹果树

cx巨巨给我的大火题.
感觉这题和上次考试gcz讲的那道有标号树的形态(不记顺序)计数问题很类似.
考虑如果对每个点对它算有贡献的其他点很麻烦,不知怎么下手.这个时候就想到换一种思路,算每一条边有多少对点经过,很自然的想到状态(dp[i][j])表示树标号到i,i子树的节点sz大小为j.这题是有标号的,先考虑无标号,那么i子树的形态一共有(j!)种.
i之上的树的形态(有先后顺序区别)有多少怎么算呢?已经到i了,说明前i个节点的形态已经确定,有(i!)种形态.
由于除了((i-1)+j)两部分以外,剩下的点有(n-j-i+1)个,这些点依次插入i的祖先们的子树中,第一次有(i-1),第二次有(i),然后(i+1,i+2,...,i-2+(n-j-i+1))乘起来就是(frac{(n-j-1)!}{(i-2)!}),分母可以和(i!)约掉.
考虑有标号,就是在(n-i)个点里选&j-1&个放在i子树里,其他的插在别的子树,那么就是乘以(C_{n-i}^{j-1})
再就是贡献还要乘上(j(n-j))
所以

[egin{align} f[i][j]=frac{(n-j-1)!}{(i-2)!}*i!*j*(n-j)*C_{n-i}^{j-1}*j!\ =(n-j)!*i*(i-1)*C_{n-i}^{j-1}*j! end{align} ]

这题注意p不是质数,不可以求逆元算.

#include<bits/stdc++.h>
#define maxn 3005
#define ll long long
using namespace std;
int inv[maxn],fac[maxn],dp[maxn][maxn];
int mod,n,ans,c[maxn][maxn];
//int C(int n,int m){return (ll)fac[n]*fac[m]%mod*fac[n-m]%mod;}
int main()
{
	cin>>n>>mod;fac[0]=inv[1]=inv[0]=1;
	for(int i=1;i<=n;i++)fac[i]=(ll)fac[i-1]*i%mod;
	//for(int i=2;i<=n;i++)inv[i]=(mod-(ll)(mod/i)*inv[mod%i]%mod)%mod;
	//for(int i=2;i<=n;i++)inv[i]=(ll)inv[i-1]*inv[i]%mod;
	for(int i=0;i<=n;i++)c[i][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dp[i][j]=(ll)fac[j]*fac[n-j]%mod*i%mod*(i-1)%mod*j%mod*c[n-i][j-1]%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)ans+=dp[i][j],ans%=mod;
	cout<<ans<<endl;
	return 0;
}

原文地址:https://www.cnblogs.com/terribleterrible/p/9846813.html