今日份难得智商在线(居然在讲评的时候听懂了概率dp ~~o(≧v≦)o)
这道题真的不错啊(当然大佬们一定觉得这是一道水题)
Analysis
预处理dp[i][j]表示i个点的森林,有j个点在第1棵树的概率,
我们有状态转移方程
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
∗
(
j
−
1
)
∗
i
n
v
[
i
]
+
d
p
[
i
−
1
]
[
j
]
∗
(
i
−
j
)
∗
i
n
v
[
i
]
dp[i][j]=dp[i−1][j−1]∗(j−1)∗inv[i]+dp[i−1][j]∗(i−j)∗inv[i]
dp[i][j]=dp[i−1][j−1]∗(j−1)∗inv[i]+dp[i−1][j]∗(i−j)∗inv[i]
(inv[i]=1/i)
Q:为什么是j-1/i?
A:因为要抛开第一棵树的根节点。如果将根节点取走,则会多得一棵树
令 f[i][j] 表示有 i 个点的树,深度不超过 j 的概率
g[i][j] 表示有 i 个点的森林,深度不超过 j 的概率
f[i][j]直接从g[i−1][j−1]转移来;
g[i][j]考虑枚举第1棵树的大小 k,我们有状态转移方程:
g
[
i
]
[
j
]
=
∑
k
=
1
i
f
[
k
]
[
j
]
∗
g
[
i
−
k
]
[
j
]
∗
d
p
[
i
]
[
k
]
g[i][j] = \sum_{k=1}^i f[k][j] ∗ g[i − k][j] ∗ dp[i][k]
g[i][j]=k=1∑if[k][j]∗g[i−k][j]∗dp[i][k]
最后只要
f
[
n
]
[
j
]
−
f
[
n
]
[
j
−
1
]
f[n][j]−f[n][j−1]
f[n][j]−f[n][j−1]就可以得到深度为j的树的概率
时间复杂度:O(n3)
Code
#include<bits/stdc++.h>
#define in read()
#define re register
#define int long long
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+(ch^48);
ch=getchar();
}
return f==1?res:-res;
}
const int N=205;
int dp[N][N],f[N][N],g[N][N];
int n,p,inv[205],res[N];
inline int ksm(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
inline void init(){
inv[1]=1;
for(re int i=2;i<=n;++i) inv[i]=ksm(i,p-2);
memset(f,-1,sizeof(f));
memset(g,-1,sizeof(g));
}
inline int solve1(int x,int h);
inline int solve2(int x,int h){
if(h<0) return 0;
if(x==0) return 1;
if(g[x][h]!=-1) return g[x][h];
g[x][h]=0;
for(re int i=1;i<=x;++i)
g[x][h]=(solve1(i,h)%p*solve2(x-i,h)%p*dp[x][i]%p+g[x][h])%p;
return g[x][h];
}
inline int solve1(int x,int h){
if(f[x][h]!=-1) return f[x][h];
f[x][h]=solve2(x-1,h-1);//f[i][j]-->g[i-1][j-1]
return f[x][h];
}
signed main(){
n=in;p=in;
if(n==1){printf("0");return 0; }
init();
dp[1][1]=1;
for(re int i=2;i<=n;++i)
for(re int j=1;j<=n;++j)
dp[i][j]=(dp[i-1][j-1]*(j-1)%p*inv[i]%p+dp[i-1][j]*(i-j)%p*inv[i]%p)%p;
f[1][1]=1;
for(re int i=2;i<=n;++i) res[i]=solve1(n,i);//选n个点做一棵树,深度不超过i的概率
res[1]=0;int ans=0;
for(re int i=2;i<=n;++i) ans=(ans+(res[i]-res[i-1])%p*(i-1)%p)%p;//长度=深度-1
cout<<(ans%p+p)%p;
return 0;
}