翻译题意:一开始只有一个点,做n-1次操作:在已生成的点中等概率的挑一个。求最后生成的树的高的期望。
我们可以设F[i][j]表示由i个点组成高不超过j的树的概率,G[i][j]表示由i个点组成高不超过j的森林的概率,此时我们考虑:在任意一种组成森林的方案中,我们用一个点连向森林中每一棵树的根节点,就正好对应了一种由i+1个点组成高不超过j+1的树的方案,即F[i][j]=G[i-1][j-1],则我们只考虑G的求法。
转移很好转:G[i][j]=F[k][j]*G[i-k][j]=G[k-1][j-1]*G[i-k][j]。
因为转移考虑到第一棵树的节点个数的分类,所以我们需要i个节点有j个节点在第一课树的概率,
转移:dp[i][j]=dp[i-1][j-1]/i+dp[i-1][j]/i;
另外注意极限情况的处理。
Code:
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
template<typename T> void read(T &num){
char c=getchar();T f=1;num=0;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
num*=f;
}
template<typename T> void qwq(T x){
if(x>9)qwq(x/10);
putchar(x%10+'0');
}
template<typename T> void write(T x){
if(x<0){x=-x;putchar('-');}
qwq(x);putchar('\n');
}
int n,p;
template<typename T> void chkmod(T &x,T y){x=(x+y)%p;}
int dp[210][210];int g[210][210];int inv[210];
inline void Pre(){
inv[1]=1;
rep(i,2,n){
inv[i]=-(1ll*(p/i)*inv[p%i])%p;
inv[i]=(inv[i]+p)%p;
}
return;
}
inline int G(int i,int j){
if(i==0)return 1;
if(j<0)return 0;
return g[i][j];
}
int main(){
read(n);read(p);
dp[1][1]=1;Pre();
if(n==1){
write(0);return 0;
}else if(n==2){
write(1);return 0;
}
rep(i,2,n){
rep(j,1,i){
int tmp=(1ll*dp[i-1][j-1]*(j-1))%p;tmp=(1ll*tmp*inv[i])%p;
chkmod(dp[i][j],tmp);
tmp=(1ll*dp[i-1][j]*(i-j))%p;tmp=(1ll*tmp*inv[i])%p;
chkmod(dp[i][j],tmp);
}
}
rep(i,1,n-1){
rep(j,0,i-1){
rep(k,1,i){
int tmp=(1ll*G(i-k,j)*G(k-1,j-1))%p;
tmp=(1ll*tmp*dp[i][k])%p;chkmod(g[i][j],tmp);
}
}
rep(j,i,n-2){g[i][j]=1;}
}
int ans=0;
rep(i,0,n-2){
int nop=(G(n-1,i)-G(n-1,i-1)+p)%p;nop=(1ll*nop*(i+1))%p;
chkmod(ans,nop);
}
write(ans);
return 0;
}