LOJ6495「雅礼集训 2018 Day1」树

Description

link

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

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

Solution

看出来是个计数题了,fi,j 表示前 i 个点 j 的深度的方案

总的方案数其实是 (n1)!

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

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

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

转移式子如下

fi,j=k=1i2l=1k(i2k1)fk,lfik,j+k=1i1l=1j(i2k1)fk,j fik,l

考虑到这个范围真很小,所以就 O(n4) 就没了,也可以通过前缀和优化至 Θ(n3)

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 @   没学完四大礼包不改名  阅读(248)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示