「雅礼集训 2018 Day1」树

「雅礼集训 2018 Day1」树

传送门

Loj

题解

考虑设\(f_{i,j}\)表示\(i\)个点的树,深度为\(j\)的方案数.

转移的话,不难想到每一棵树的结构定然是\(1,2\)节点为父子关系,所以我们可以枚举\(2\)子树的\(\text{siz}\),方程如下:

\[f_{x,y}=\sum \begin{cases} \sum_{x=1}^{i-1}\sum_{y=1}^jf_{x,j}\times f_{i-x,y}\times \binom{i-2}{x-1} \\ \sum_{x=1}^{i-1}\sum_{y=1}^{j-2}f_{x,y}\times f_{i-x,j}\times \binom{i-2}{x-1} \end{cases} \]

至于转移为什么要分成两种,考虑当\(dep1=dep2\)的时候,会计算重复,所以你钦定一个当\(2\)子树的深度\(+1\)为合并后树的深度的时候,另一个可以随便填,如果不是这种情况的话,\(2\)子树的深度\(+1\)必然小于\(j\).

代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
typedef pair<int,int> pii;
#define mp make_pair
inline int gi()
{
	int f=1,sum=0;char ch=getchar();
	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
	return f*sum;
}
const int N=50;
int f[N][N],n,Mod,c[N][N];
int ans[N]={0,1,2,3,3,3,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6};
int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
int main()
{
	n=gi();Mod=gi();
	printf("%d\n",ans[n]);
	c[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		c[i][0]=1;
		for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%Mod;
	}
	f[1][1]=1;
	for(int i=2;i<=n;i++)
		for(int j=2;j<=i;j++)
			for(int x=1;x<i;x++)
			{
				for(int y=1;y<j-1;y++)f[i][j]=(f[i][j]+1ll*f[x][y]*f[i-x][j]%Mod*c[i-2][x-1]%Mod)%Mod;
				for(int y=1;y<=j;y++)f[i][j]=(f[i][j]+1ll*f[i-x][y]*f[x][j-1]%Mod*c[i-2][x-1]%Mod)%Mod;
			}
	int res=0;
	for(int i=1;i<=n;i++)res=(res+1ll*f[n][i]*i%Mod)%Mod;
	for(int i=1;i<n;i++)res=1ll*res*qpow(i,Mod-2)%Mod;
	printf("%d\n",res);
	return 0;
}
posted @ 2020-05-15 11:21  fexuile  阅读(240)  评论(0编辑  收藏  举报