头像

Cyan

四川成都

深度强化学习炼丹师

2019第十届蓝桥杯国赛-G排列数

2019第十届蓝桥杯国赛-G排列数

2021-07-03 · 22次阅读 · 原创 · 数据结构与算法

题面

【问题描述】

在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素, 或者同时大于两边的元素。

对于一个 1 ∼ n 的排列,如果可以将这个排列中包含 t 个折点,则它称为 一个 t + 1 单调序列。

例如,排列 (1, 4, 2, 3) 是一个 3 单调序列,其中 4 和 2 都是折点。

给定 n 和 k,请问 1 ∼ n 的所有排列中有多少个 k 单调队列?

【输入格式】

输入一行包含两个整数 n, k。

【输出格式】

输出一个整数,表示答案。答案可能很大,你可需要输出满足条件的排列 数量除以 123456 的余数即可。

【样例输入】

4 2

【样例输出】

12

【评测用例规模与约定】

对于 20% 的评测用例,1 ≤ k ≤ n ≤ 10;

对于 40% 的评测用例,1 ≤ k ≤ n ≤ 20;

对于 60% 的评测用例,1 ≤ k ≤ n ≤ 100;

对于所有评测用例,1 ≤ k ≤ n ≤ 500。

思路

1. 暴力骗分

全排列,再检测是否满足 k-1 个折点的情况,只能过 20 % 的样例。

2. 动态规划

传送门 排列数

代码

#include<iostream> #include<cstring> using namespace std; const int N = 505, MOD = 123456; int f[2][N][N][2], s[2][N][N][2], n, m; int main() { while (cin >> n >> m) { memset(f, 0, sizeof f); f[1][1][1][0] = 1; f[0][1][1][0] = f[0][2][1][1] = 1; s[0][1][1][0] = s[0][2][1][0] = s[0][2][1][1] = 1; for (int i = 3; i <= n; i++) { memset(f[i & 1], 0, sizeof f[0]); memset(s[i & 1], 0, sizeof s[0]); for (int j = 1; j <= i; j++) { for (int k = 1; k <= i && k <= m; k++) { f[i & 1][j][k][1] = (f[i & 1][j][k][1] + s[i - 1 & 1][j - 1][k][1] + s[i - 1 & 1][j - 1][k - 1][0]) % MOD; f[i & 1][j][k][0] = (f[i & 1][j][k][0] + s[i - 1 & 1][i - 1][k][0] - s[i - 1 & 1][j - 1][k][0]) % MOD; f[i & 1][j][k][0] = (f[i & 1][j][k][0] + s[i - 1 & 1][i - 1][k - 1][1] - s[i - 1 & 1][j - 1][k - 1][1]) % MOD; s[i & 1][j][k][0] = (s[i & 1][j - 1][k][0] + f[i & 1][j][k][0]) % MOD; s[i & 1][j][k][1] = (s[i & 1][j - 1][k][1] + f[i & 1][j][k][1]) % MOD; } } } cout << ((s[n & 1][n][m][0] + s[n & 1][n][m][1]) % MOD + MOD) % MOD << endl; } return 0; }

标题: 2019第十届蓝桥杯国赛-G排列数
链接: https://www.fightingok.cn/detail/118
更新: 2022-09-18 22:40:24
版权: 本文采用 CC BY-NC-SA 3.0 CN 协议进行许可