1 条题解
-
0
H. 樱子的测试 - 详细题解
问题重述
给定一个长度为 的数组 ,可以进行任意次操作:选择 的元素,将其减去 。对于每个询问的 ,求操作后数组可能的最小中位数。
关键观察
观察 1:操作的效果
对于固定的 ,反复执行 (当 时)相当于:
因为我们可以不断减去 直到 。
因此,最终数组中的每个元素都变为 ,取值范围为 。
观察 2:最小化中位数
为了最小化中位数,我们希望尽可能多的元素变小。
由于最终值只能是 到 ,我们无法改变模运算的结果,因此中位数的最小值就是最终数组的中位数。
观察 3:判断某个数 是否可以是中位数
中位数定义为排序后第 个元素(-based 索引)。对于偶数 ,取第 个位置(-based),等价于 -based 索引 。
设 (-based)。
如果中位数 ,则至少需要 个元素 。
因此,我们需要计算有多少个 满足 。
观察 4:计算满足条件的元素个数
对于固定的 和 , 等价于存在整数 使得:
因为 的范围是 ,所以 的范围是 。
因此,满足条件的元素个数为:
$$cnt = \sum_{k=0}^{\lfloor n/x \rfloor} \text{count}(k \cdot x, k \cdot x + m) $$其中 表示原数组中值在 内的元素个数。
观察 5:预处理计数数组
由于 ,我们可以构建一个计数数组 表示值为 的元素个数,然后计算前缀和:
则 (当 时)。
观察 6:二分查找最小中位数
对于固定的 ,我们可以二分查找最小的 使得满足条件的元素个数 。
二分范围:。
检查函数 :计算 并与 比较。
观察 7:预处理所有
由于 ,且 的总和不超过 ,我们可以预处理所有 的答案。
时间复杂度分析:
对于每个 ,需要遍历 到 ,共 个区间。二分查找需要 次检查。
因此总复杂度:
$$\sum_{x=1}^{n} \frac{n}{x} \cdot \log n \approx n \log n \cdot \sum_{x=1}^{n} \frac{1}{x} = n \log n \cdot H_n \approx n \log^2 n $$由于 ,$n \log^2 n \approx 10^5 \times 17^2 \approx 2.89 \times 10^7$,可以接受。
算法步骤
- 读入 和数组
- 构建计数数组 ,并计算前缀和
- 对于每个 从 到 :
- 二分查找最小的 ()使得满足 的元素个数
- 将结果存入
- 对于每个查询 ,输出
完整代码
#include <bits/stdc++.h> using namespace std; int main() { ios_base::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n, q; cin >> n >> q; vector<int> a(n); vector<int> cnt(n + 1, 0); for (int i = 0; i < n; i++) { cin >> a[i]; cnt[a[i]]++; } // 前缀和 for (int i = 1; i <= n; i++) { cnt[i] += cnt[i - 1]; } int target = n / 2; // 0-based 中位数索引 vector<int> res(n + 1, 0); // 预处理所有 x for (int x = 1; x <= n; x++) { int l = 0, r = x; while (l < r) { int mid = (l + r) / 2; // 计算有多少个元素 mod x <= mid int total = cnt[mid]; // k = 0 的情况 for (int k = 1; k * x <= n; k++) { int left = k * x; int right = min(k * x + mid, n); total += cnt[right] - cnt[left - 1]; } if (total - 1 >= target) { r = mid; } else { l = mid + 1; } } res[x] = l; } // 处理查询 while (q--) { int x; cin >> x; cout << res[x] << " "; } cout << "\n"; } return 0; }
示例验证
测试用例 1
,
- :,所有元素变为 ,中位数
- :,排序后 ,中位数
- :,排序后 ,中位数
- :,排序后 ,中位数
- :,排序后 ,中位数
输出:
0 1 1 1 2✓
测试用例 2
,
- :,排序后 ,中位数
- :所有元素变为 ,中位数
- :,排序后 ,中位数
输出:
1 0 2✓
时间复杂度
- 预处理:
- 每个查询:
- 总复杂度:
关键公式总结
$$\boxed{\text{中位数条件: } \#\{i : a_i \bmod x \le m\} \ge \lfloor n/2 \rfloor + 1} $$$$\boxed{\#\{i : a_i \bmod x \le m\} = \sum_{k=0}^{\lfloor n/x \rfloor} \text{count}(k \cdot x, k \cdot x + m)} $$
- 1
信息
- ID
- 6332
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者