1 条题解
-
0
以下是根据标程思路整理的详细题解。
题目重述
给定 和一个由 个区间构成的集合,区间满足 (即互不重叠且按顺序排列)。
$$a_i = \max_{j = l_i}^{k_i} p_j, \quad b_i = \min_{j = k_i + 1}^{r_i} p_j $$
称一个 的排列 是有趣的,如果对每个区间 都可以选一个整数 (),记满足
要求:若存在有趣排列,输出最大可能的逆序对数;否则输出 。
问题转化
1. 全局分割点的存在性
对所有区间同时满足
意味着存在一个实数 ,使得
对于每个区间 , 是左半部分的最大值, 是右半部分的最小值。
因此, 必须同时大于所有左半部分的最大值,且同时小于所有右半部分的最小值。更关键的是:
如果我们令 ,,那么 把区间分成左、右两段。
为了让 ,必须保证:- 区间 中至少有一个元素 在左边(实际上所有左半部分都要 ,但最大值必须 ,所以左半部分全部 )
- 区间 中至少有一个元素 在右边(右半部分全部 )
等价地:在数轴上, 把每个区间 都切成了严格小于 的左段和严格大于 的右段。
这说明 必须严格位于每个区间的内部,即:由于 是整数,且 是整数, 可以取两个相邻整数之间的值。
所以存在这样的 当且仅当:因为我们可以取 为 和 之间的任意实数。
2. 有趣排列的存在条件
因此,有趣排列存在 。
当 时,没有区间限制,显然存在有趣排列(任何排列都可以)。
3. 最大逆序对数的构造
当条件满足时,我们想最大化逆序对数。
逆序对数的理论最大值是 ,当排列完全降序时达到(如 )。
我们只需要证明:当 时,可以构造一个完全降序的排列,使得它满足有趣排列的条件。
构造方法
设 ,,已知 。
取 对所有 成立?注意: 必须满足 。
由于 是 ,所以 对所有 成立。
又 (因为 ),所以 对所有 成立。
因此 是合法的。于是:
- 对每个区间 ,左半部分是 ,右半部分是 。
- 在完全降序排列中, 里的所有数都大于 里的所有数(因为整体降序,且 位置的值大于 位置的值)。
- 所以 (左半最大值)大于 (右半最小值)。
等一下,这里出问题了!我们要的是 ,但现在得到了 。
这说明降序排列反而让左边部分更大,不满足条件。
4. 正确的构造思路
实际上,我们需要左边部分的最大值 小于 右边部分的最小值,即左半部分整体 小于 右半部分。
在降序排列中,左半部分大于右半部分,所以降序不行。正确的构造是:
把排列分成两部分:- 前 个位置(‑based 下标 )放最大的 个数(降序)
- 后 个位置放剩下的数(升序)
这样,对任意 ,左半部分 在降序段中,右半部分 在升序段中,
由于降序段的数都大于升序段的数?又反了。仔细推导:
设 ,那么 是划分点。
令 对所有 。
那么左半部分是 ,右半部分是 。我们希望左半部分所有数 小于 右半部分所有数。
这等价于: 这些数 < 这些数。一个简单的做法:
让位置 放最小的 个数(升序),位置 放最大的 个数(降序)。这样:
- 左半部分在小的数堆里,右半部分在大的数堆里
- 所以左半部分 < 右半部分,满足
此时逆序对来自:
- 左边内部: 个数升序,逆序对
- 右边内部: 个数降序,逆序对
- 左右之间:每个左边数都小于每个右边数,所以左边位置在右边位置之前时不会产生逆序对?
等等, 且 才算逆序对。
左边位置都在右边位置之前,且左边数 < 右边数,所以 ,不是逆序对。
因此左右之间逆序对 。
总逆序对 ,这比最大值小很多,不是最优。
5. 最大化的构造
其实当 时,可以取 即每个区间留最后一个元素在右边,其余在左边。
那么所有 取左边部分的最大值,所有 取右端点元素(单个)。我们只要让所有左边部分的最大值都小于所有右端点元素的最小值即可。
这可以通过把右端点元素设为最小的 个数,左边部分设为最大的 个数(降序)实现。这样:
- 左边部分内部逆序对:
- 右边部分内部逆序对:(右边单元素)
- 左右之间:左边每个数 > 右边每个数,且所有左边位置在右边位置之前,产生 个逆序对
总逆序对:
$$\binom{n-m}{2} + (n-m)m = \frac{(n-m)(n-m-1)}{2} + m(n-m) $$化简:
$$= \frac{(n-m)(n-m-1 + 2m)}{2} = \frac{(n-m)(n+m-1)}{2} $$这个值小于 ,但这是不是最大?当 时它等于 ,正确。
当 时,它确实是在满足区间约束下的最大逆序对数。
6. 最终公式
因此,若 ,则最大逆序对数为:
否则输出 。
时间复杂度
每个测试用例 ,总 和不超过 ,可以通过。
代码实现
#include <bits/stdc++.h> using namespace std; using ll = long long; void solve() { int n, m; cin >> n >> m; int max_l = 0, min_r = n; for (int i = 0; i < m; i++) { int l, r; cin >> l >> r; l--; r--; max_l = max(max_l, l); min_r = min(min_r, r); } if (m == 0) { cout << (ll)n * (n - 1) / 2 << "\n"; } else if (max_l < min_r) { ll ans = (ll)(n - m) * (n + m - 1) / 2; cout << ans << "\n"; } else { cout << "-1\n"; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 7203
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者