1 条题解
-
0
核心思路
1. 好排列的等价条件
经过推导(原题提示),好排列等价于没有长度 的下降子序列的排列。
这进一步等价于排列能拆分成至多两个递增子序列。
2. 组合表示
这样的排列可以用 Catalan 数的某种扩展来计数。
对于长度为 的排列,能拆分成两个递增子序列的排列个数就是 Catalan 数 的 倍?不,实际上就是 相关的某种结构。
更准确地说,这类排列与 Ballot 序列 或 Dyck 路径 双射。
3. 代码中的关键函数
int F(int x, int y) { return x <= y && y <= n ? (C(2 * n - x - y - 1, n - x - 1) - C(2 * n - x - y - 1, n - y - 2) + mod) % mod : 0; }这个
F(x, y)函数计算的是:- 在网格图中从 到 ,且不穿过对角线 的路径数
- 这对应着好排列的某种计数
4. 算法流程
- 预处理阶乘和逆元,用于组合数计算
- 对每个测试用例:
- 读入排列
- 计算前缀最大值
mx[i] - 计算后缀最小值
mn[i]
- 逐位确定:
- 对于位置 ,假设前 位与 相同,第 位选择比 大的数
- 用
F(i-1, mx[i]+1)计算这种情况下剩余位置能形成好排列的方案数 - 累加到答案
- 提前终止:
- 如果发现
mx[i-1] > a[i] && a[i] > mn[i+1],说明后续无论如何都会破坏好排列条件,直接退出
- 如果发现
关键点解释
为什么
F(i-1, mx[i]+1)能计数?i-1表示已经确定了前 个位置mx[i]+1表示第 位至少要选的最小值(为了字典序大于 )F(x,y)计算的是从状态 出发能形成好排列的方案数
提前终止条件
if (mx[i-1] > a[i] && a[i] > mn[i+1]) break;这个条件检测是否出现了「长度 的下降子序列」:
mx[i-1]是前缀最大值a[i]是当前值mn[i+1]是后缀最小值 如果mx[i-1] > a[i] > mn[i+1],那么(mx[i-1], a[i], mn[i+1])就构成了长度为 3 的下降子序列,后续无论如何都不能形成好排列。
举例验证
对于样例:
n=3, a=[1,3,2]计算过程:
- i=1: F(0,2) = 计算从(0,2)到(3,3)的合法路径数
- i=2: F(1,4) = 0(y>n)
- i=3: 不计算,因为前两位已经确定
最终得到答案 3。
时间复杂度
- 预处理阶乘:
- 每个测试用例:
- 总复杂度:,可以通过 的数据
总结
这道题的核心是将「好排列」的组合结构转化为网格路径计数问题,然后利用字典序逐位确定的方法统计方案数。代码中的
F(x,y)函数是关键,它用组合数相减的方法计算了不穿过对角线的路径数,这正好对应了好排列的计数。
- 1
信息
- ID
- 4446
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者