倒着考虑,从一个给定的
A
N
A_N
AN不断删除字符能得到多少种不同的方案。为了不计重复,我们要求每次删掉一个字符
x
i
x_i
xi时,满足
x
i
+
1
≠
x
i
x_{i+1}\neq x_i
xi+1=xi,那么合法的条件是
∃
j
≥
i
,
x
i
=
x
i
+
1
=
.
.
.
=
x
j
,
x
j
+
1
<
x
i
\exist j\geq i,x_i=x_{i+1}=...=x_j,x_{j+1}<x_i
∃j≥i,xi=xi+1=...=xj,xj+1<xi(假设最后补一个
0
0
0)。
考虑计数,设
F
[
i
]
[
j
]
F[i][j]
F[i][j]表示当前序列长度为
i
i
i,元素在
1
1
1到
j
j
j之间,枚举最后一个删掉的位置为
k
k
k,权值为
l
l
l,那么
1
1
1到
k
−
1
k-1
k−1间的元素均大于
l
l
l,而
k
+
1
k+1
k+1到
i
i
i间无限制,两部分互相独立,可以用组合数合并。直接转移是四次方的,但注意到
l
l
l与后半部分无关,可以预处理转移加速。
时间复杂度
O
(
N
2
K
)
\mathcal O(N^2K)
O(N2K)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int MOD;
ll C[305][305];
int f[305][305],sum[305][305];
void pre(int n) {
for(int i=0;i<=n;i++) C[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
int main() {
int n,k;
scanf("%d%d%d",&n,&k,&MOD);
pre(n);
for(int i=1;i<=k+1;i++) {
f[0][i]=1;
sum[0][i]=i;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=k+1;j++)
for(int l=1;l<=i;l++)
f[i][j]=(f[i][j]+(ll)sum[l-1][j-1]*f[i-l][j]%MOD*C[i-1][l-1])%MOD;
for(int j=1;j<=k+1;j++) sum[i][j]=(sum[i][j-1]+f[i][j])%MOD;
}
printf("%d\n",f[n][k+1]);
return 0;
}