LXXX.[AGC024E] Sequence Growing Hard
首先,我们肯定能想到从第一个序列开始,依次加入一个新数得到下一个序列,同时还要保证字典序递增。我们如果让新数递增的话,就可以DP了。
我们首先观察往一个序列中加入一个不大于最大值的数会有多少种可能:
我们在\(1323\)中加入一个\(3\),
位置 | 结果 |
---|---|
开头 | \(31323\) |
第一个数后 | \(13323\) |
第二个数后 | \(13323\) |
第三个数后 | \(13233\) |
第四个数后 | \(13233\) |
明显所有结果全都符合要求,但是有重复计算的地方。
我们可以强制要求加数必须加在连续一段相同的数的后面,在上例中就是你无法在第一个、第三个数后面添加\(3\)。
我们可以设\(f[i][j][k]\)表示当前处理完成了前\(i\)个串,计划往第\(i+1\)个串里加入一个数\(j\),并且有\(k\)个位置可以加入\(j\)的方案数。
则\(f[i][j][k]\)可以转移到:
-
如果\(k>0\),可以转移到\(f[i][j][k-1]\),它的意义是我们跳过第\(k\)个位置不加。
-
如果\(k=0\),可以转移到\(f[i][j+1][i]\),它的意义是第\(j\)个数已经全部加完,可以尝试\(j+1\)了。它有\(i\)个位置可以填,因为没有任何一个数与\(j+1\)相同,它可以直接加到任何数后面。
-
无论何时,都可以转移到\(f[i+1][j][k]\),意义是我们在第\(k\)个位置加入一个数。这共有\(k+1\)种加法,因为我们还有一种在开头的加法是一直可以的。
复杂度\(O(n^3)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,p,f[310][310][310];
//f[i][j][k]:we've finished constructing the first i sequences, now we're going to add the number j into the i+1-th sqeuence, and there're k places to add j into
int main(){
scanf("%d%d%d",&n,&m,&p),f[0][1][0]=1;
for(int i=0;i<=n;i++)for(int j=1;j<=m;j++)for(int k=i;k>=0;k--){
if(k)(f[i][j][k-1]+=f[i][j][k])%=p;//we decide not to add j to the k-th place, so we could add it to the (k-1)-th place.
else(f[i][j+1][i]+=f[i][j][k])%=p;//we have tried every place j could be added to, now it's time to try j+1, which could be added into any place
(f[i+1][j][k]+=1ll*f[i][j][k]*(k+1)%p)%=p;//we decide to add j to the k-th place, and there are (k+1) places for us to add (including the last one)
}
printf("%d\n",f[n][m+1][n]);//all n sequences've been constructed, and all number've been tried
return 0;
}