题目在这里呀!
啊啊啊准备了一个月期末考的我终于飘回来啦~~
真可谓在学OI的时候忘不了文化课,然后在学文化课的时候又想念编程啦qwq
那那那开始我的七月第一篇题解?
(我七月想写很多很多题解的w)
考虑 N +1 个数组 {A0,A1,…,AN}。
其中 Ai 的长度是 i,Ai 内的所有数字都在 1 到 K 之间。 Ai−1 是 Ai 的子序列,即 Ai 删一个数字可以得到 Ai−1。 Ai 的字典序大于 Ai−1。
输入 N,K,M 问序列个数模 M。
好像有两种方法?
反倒我还没理解那个好理解的方法,以后补全吧
很容易想到dp吧。
那么那么
考虑每次从前面那个状态插入一个数,那么插入位置右边的那个数要严格小于插入的数。
所以我们去想想状态
fi,j,p f i , j , p 表示当前长度为i,取到数字j,有p个位置可插的方案数。
那么考虑转移,
一种情况,不插在这个位置,那么 fi,j,p−1 f i , j , p − 1 += fi,j,p f i , j , p
还有就是插在这个位置,那么 fi+1,j,p f i + 1 , j , p += fi,j,p f i , j , p
或者这个数字不插了, fi,j+1,i f i , j + 1 , i += fi,j,p f i , j , p 第三维为i是因为j加1了所以所有数字都比它小。
答案就是 fn,k+1,n f n , k + 1 , n
然后…就好了?代填坑(另一种方法)
//Suplex
#include
#include
#include
#include
#include
using namespace std;
int n,k,m;
long long f[333][333][333];
int main()
{
scanf("%d%d%d",&n,&k,&m);
f[0][1][0]=1;
for(int i=0;i<=n;i++)
for(int j=1;j<=k;j++)
for(int p=i;p>=0;p--){
if(p) (f[i][j][p-1]+=f[i][j][p]) %= m;
else (f[i][j+1][i]+=f[i][j][p]) %= m;
(f[i+1][j][p]+=f[i][j][p]*(p+1)) %= m;
}
printf("%lld\n",f[n][k+1][n]);
return 0;
}