给出一种生成长度为
- 先生成一个长度为
的排列 ,然后对于 ,将 分成一块。 - 有
个指针,开始指向这 个块的开头。 - 每次选择这
个指针指向的数中最小的并将其放在 的末尾,将这个指针向后移一位,如果移出块了,将指针删除。 - 回到 2 操作直到所有指针均被删除。
现在要你求出,这样的方式共能生成多少种本质不同的排列
首先我们不难发现这个序列在一定程度上肯定是有单调性的,因为本身这个生成的方式就是一个单调的过程。
我们发现对于一个块
那么也就是说,我们现在的情况是,有一些长度为
也就是说我们需要找到其充要条件。我们发现,其实长度为
现在我们只要将划分完的块拼起来就可以了。也就是说,一个序列合法的充要条件是:存在一种划分方案,使得划分后每个块大小小于等于
我们思考这东西怎么计算,我们先考虑如果划分方案已经给出,数字随便填,这样有几种方案。
我们发现,一些限制就是:dp 了。
我们令
然后就可以愉快地转移啦!
namespace dfcmd {
typedef long long LL;
const int maxn = 2000;
const int base = maxn * 3 + 2;
int n, mod;
int f[maxn * 3 + 5][maxn * 6 + 5];
inline void add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
}
int main() {
read(n), read(mod);
n *= 3;
f[0][base] = 1;
for (register int i = 0; i < n; ++i) {
for (register int j = 1; j <= base << 1; ++j) {
add(f[i + 1][j + 1], f[i][j]);
add(f[i + 2][j - 1], (LL) f[i][j] * (i + 1) % mod);
add(f[i + 3][j], (LL) f[i][j] * (i + 1) * (i + 2) % mod);
}
}
int ans = 0;
for (int i = base; i <= base << 1; ++i) {
add(ans, f[n][i]);
}
writeln(ans);
return 0;
}
}