把问题看成每次向序列中添加一个
[1,K]
[
1
,
K
]
的数,假设我们要填的数是
x
x
,因为要保证字典序变大,那么所填的位置往后的第一个非数必须
<x
<
x
<script type="math/tex" id="MathJax-Element-80">
x
x
中的任意位置再填一个都是等价的,所以等同于只能在一个
<x
<
x
<script type="math/tex" id="MathJax-Element-83">
x
x
。
那我们把填一个数看成一个二元组,表示在第
t
t
次填了数,假设把这个
w
w
填的位置后面的那个的数看成它的父亲,那么就有了一个树型结构。(可以把序列末尾看成一个
(t=0,w=0)
(
t
=
0
,
w
=
0
)
)
那么问题就转化成了计满足一下条件的树的个数:
1.
troot=0,wroot=0
t
r
o
o
t
=
0
,
w
r
o
o
t
=
0
。
2.
tx
t
x
互不相同,
wx∈[0,K]
w
x
∈
[
0
,
K
]
。
3.
tx>tfax
t
x
>
t
f
a
x
。
4.
wx>wfax
w
x
>
w
f
a
x
。
于是我们可以设
fi,j
f
i
,
j
表示有
i
i
个点,的树的个数。考虑转移,我们可以给根节点添加一个大小为
k
k
,的子树,因为子树顺序的问题,我们可以钦定这棵新子树的根是所有子树的根中最先添加的,那么也就是把剩下
k−1
k
−
1
个元素的序列插进去组成一个
i−2
i
−
2
的序列(整颗树的根和该子树的根已经确定),转移方程就是:
后面那个求和用一下前缀和就能优化到 O(n2K) O ( n 2 K ) 了。
代码:
#include<iostream>
#include<cstdio>
#define ll long long
#define N 310
using namespace std;
ll C[N][N],f[N][N],s[N][N];
int n,K,mod;
int main()
{
scanf("%d%d%d",&n,&K,&mod);
C[0][0]=1;
for(int i=1;i<=n;C[i][0]=1,i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(int j=0;j<=K;j++)
f[1][j]=1,s[1][j]=K-j+1;
for(int i=2;i<=n+1;i++)
{
for(int j=0;j<=K;j++)
for(int k=1;k<i;k++)
f[i][j]=(f[i][j]+f[i-k][j]*s[k][j+1]%mod*C[i-2][k-1])%mod;
for(int j=K;j>=0;j--)
s[i][j]=(f[i][j]+s[i][j+1])%mod;
}
printf("%lld",f[n+1][0]);
return 0;
}


被折叠的 条评论
为什么被折叠?



