解析
比较巧妙的一道题。
难点在于对题意的转化。
关键性质:符合要求的点等价于与笛卡尔树上深度为 m m m 的点。
原因也较为显然,考虑一个特定的点 x x x,当枚举全局最大值时,其会对 x x x 产生贡献,且最大值另一侧就和 x x x 没有关系了,向有 x x x 的一侧递归寻找最大值计算贡献,这个过程和笛卡尔树的构造是一样的。
问题就转化为了:给定一个二叉树,求第
m
m
m 层有
k
k
k 个节点的方案数。
这就是一个喜闻乐见的dp了。
设计状态
d
p
i
,
j
,
s
dp_{i,j,s}
dpi,j,s 表示子树根节点深度为
j
j
j,子树大小为
i
i
i,且子树内有
s
s
s 个好点的方案数。
就有转移:
f
i
,
j
,
s
=
∑
a
=
0
i
−
1
∑
b
=
0
a
(
i
−
1
a
)
f
a
,
j
+
1
,
b
×
f
i
−
1
−
a
,
j
+
1
,
s
−
b
−
[
j
=
m
]
f_{i,j,s}=\sum_{a=0}^{i-1}\sum_{b=0}^a\binom{i-1}{a}f_{a,j+1,b}\times f_{i-1-a,j+1,s-b-[j=m]}
fi,j,s=a=0∑i−1b=0∑a(ai−1)fa,j+1,b×fi−1−a,j+1,s−b−[j=m]
直接做是
O
(
n
5
)
O(n^5)
O(n5) 的。
然后似乎也没有什么办法优化这个东西…于是就卡一卡常好了,调整一些变量的枚举上界,再直接让
f
i
,
m
,
1
=
i
!
f_{i,m,1}=i!
fi,m,1=i!。
然后就能卡过去了,最慢的点
1.7
s
1.7 s
1.7s。
(真不理解出题人为什么不能把范围改成
80
80
80 之类的,这种东西比赛时就算想到了也很可能不敢写吧…)
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;
const int N=105;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int mod;
inline ll ksm(ll x,ll k){
ll res(1);
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
int n,m,cnt;
ll c[N][N],jc[N];
void init(int n){
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
return;
}
ll f[N][N][N];
signed main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read();m=read();cnt=read();mod=read();
init(n);
for(int i=0;i<=n;i++) f[i][m][min(i,1)]=jc[i];
for(int j=m-1;j>=1;j--){
f[0][j][0]=1;
for(int i=1;i+(j-1)<=n;i++){
for(int k=0;k<=min(cnt,i);k++){
for(int a=0;a<i;a++){
for(int b=0;b==0||b+m-(j+1)<=a;b++){
(f[i][j][k]+=f[a][j+1][b]*f[i-1-a][j+1][k-b]%mod*c[i-1][a])%=mod;
//printf("(%d %d %d) -< (%d %d %d)*(%d %d %d) add=%lld\n",i,j,k,a,j+1,b,i-1-a,j+1,m-)
}
}
}
}
}
//for(int j=1;j<=m;j++){
// for(int i=0;i<=n;i++){
// for(int k=0;k<=cnt;k++) printf("siz=%d dep=%d num=%d f=%lld\n",i,j,k,f[i][j][k]);
// }
//}
printf("%lld\n",f[n][1][cnt]);
return 0;
}
/*
*/