题目在这里呀!
啊啊啊准备了一个月期末考的我终于飘回来啦~~
真可谓在学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 <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
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;
}