LOJ6495「雅礼集训 2018 Day1」树

Description

link

有一棵 \(n\) 个点的有根树,点编号为 \(1\)\(n\),其中 \(1\) 号点为根,除 \(1\) 号点外, \(i\) 号点的父亲在至 \(1\)\(i\) 内均匀随机。

定义一棵树的深度为所有节点到根路径上节点数的最大值,求这棵树的期望深度。

Solution

看出来是个计数题了,\(f_{i,j}\) 表示前 \(i\) 个点 \(j\) 的深度的方案

总的方案数其实是 \((n-1)!\)

因为是个计数,所以应该往方案数乘法那边考虑

显然这个 \(1,2\) 两个点的形态是固定的,然后就考虑最大深度的构成在哪里

这里指是在 \(2\) 的子树里面还是没有在就好,这里还是个分组的问题

转移式子如下

\[f_{i,j}=\sum_{k=1}^{i-2} \sum _ {l=1}^k \binom {i-2}{k-1} f_{k,l} f_{i-k,j}+\sum_{k=1}^{i-1}\sum_{l=1}^j \binom {i-2}{k-1} f_{k,j}\ f_{i-k,l} \]

考虑到这个范围真很小,所以就 \(O(n^4)\) 就没了,也可以通过前缀和优化至 \(\Theta(n^3)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=40;
	int n,p,f1[N][N],fac[N],inv[N];
	inline int ksm(int x,int y)
	{
		int res=1; for(;y;y>>=1,x=x*x%p) if(y&1) res=res*x%p;
		return res;
	}
	inline int add(int x,int y){return x+y>=p?x+y-p:x+y;}
	inline int del(int x,int y){return x-y<0?x-y+p:x-y;}
	inline int C(int n,int m){return fac[n]*inv[m]%p*inv[n-m]%p;}
	double c[N][N],f2[N][N];
	signed main()
	{
		n=read(); p=read(); fac[0]=inv[1]=inv[0]=1;
		if(n==1) cout<<"1\n1\n",exit(0); c[0][0]=1;
		for(int i=1;i<=24;++i)
		{
			c[i][0]=1;
			for(int j=1;j<=i;++j) c[i][j]=c[i-1][j]+c[i-1][j-1];
		} 
		for(int i=1;i<N;++i) fac[i]=fac[i-1]*i%p;
		for(int i=2;i<N;++i) inv[i]=p-p/i*inv[p%i]%p;
		for(int i=1;i<N;++i) inv[i]=inv[i-1]*inv[i]%p;
		f1[1][1]=f2[1][1]=f2[2][2]=f1[2][2]=1;
		for(int i=3;i<=n;++i)
		{
			for(int j=2;j<=i;++j) 
			{
				for(int k=1;k<=i-2;++k)
				{
					for(int l=1;l<=min(j-2,k);++l)
					{
						f1[i][j]=add(f1[i][j],f1[k][l]*f1[i-k][j]%p*C(i-2,k-1)%p);
						f2[i][j]+=f2[k][l]*f2[i-k][j]*c[i-2][k-1];
					} 
				}
				for(int k=1;k<=i-1;++k)
				{
					for(int l=1;l<=j;++l)
					{
					 	f1[i][j]=add(f1[i][j],f1[k][j-1]*f1[i-k][l]%p*C(i-2,k-1)%p);
					 	f2[i][j]+=f2[k][j-1]*f2[i-k][l]*c[i-2][k-1];
					}
				}
			}
		}
		int ans2=0; double ans1=0;
		for(int i=1;i<=n;++i) ans2=add(ans2,i*f1[n][i]%p),ans1+=i*f2[n][i];
		for(int i=1;i<=n-1;++i) ans1/=(double)i; 
		ans2=ans2*ksm(fac[n-1],p-2)%p;
		printf("%lld\n%lld\n",(int)(ans1+0.5),ans2);
		return 0;
	}
}
signed main(){return yspm::main();}
posted @ 2020-08-02 14:37  yspm  阅读(228)  评论(0编辑  收藏  举报