zoukankan      html  css  js  c++  java
  • 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 inom {i-2}{k-1} f_{k,l} f_{i-k,j}+sum_{k=1}^{i-1}sum_{l=1}^j inom {i-2}{k-1} f_{k,j} f_{i-k,l} ]

    考虑到这个范围真很小,所以就 (O(n^4)) 就没了

    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
    1
    ",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
    %lld
    ",(int)(ans1+0.5),ans2);
    		return 0;
    	}
    }
    signed main(){return yspm::main();}
    
  • 相关阅读:
    动态内存开辟(一)
    结构体,联合体,枚举,typedef
    练习使用C++的string类
    WIN10 + Qt 5.14(MSVC 2017,32bit) + APP项目(minGW-7.3.0 32bit)+glog
    扫雷游戏
    最小栈实现
    快速排序算法
    c++语句(循环)
    C++ 存储类
    C++ 运算符
  • 原文地址:https://www.cnblogs.com/yspm/p/13418723.html
Copyright © 2011-2022 走看看