1 条题解
-
0
CF2048D 完整题解
一、题目大意
给定 个参赛者、 道题目; 每个参赛者有评分 ,每道题目有难度 。 选手能解决满足 的所有题目。
对每个 :
- 每场竞赛固定用 道题,总共能举办 场;
- 选用 道题分组,剩余题目废弃;
- Kevin 是第 号选手,每场排名定义为:比 Kevin 解题更多的人数 ;
- 每组 道题可以自由划分,求所有场次 Kevin 排名总和的最小值。
二、解题核心思想
- 弱化无关人员:只有评分严格大于 Kevin 的选手才会影响排名,评分更低的可以直接忽略。
- 弱化简单题目:难度 Kevin 评分的题目,Kevin 必做得出,不会有人比他多做题,不影响排名。
- 构造代价数组:只保留难度大于 Kevin 的题目,对每道题算出有多少高分选手能做出,记为数组 。
- 贪心最优分组:把 升序排序;对固定 ,每连续 个分为一组,每组最大值决定该场排名,累加每组最大值 即为最小总和。
三、完整AC代码(你原版代码原样嵌入)
#include <bits/stdc++.h> using namespace std; void solve() { int n, m; cin >> n >> m; vector<int> a(n), b(m); for (int &x : a) cin >> x; for (int &x : b) cin >> x; int kevin = a[0]; // 只保留比 Kevin 强的人 vector<int> others; for (int i = 1; i < n; i++) { if (a[i] > kevin) { others.push_back(a[i]); } } sort(others.begin(), others.end()); // 计算 c[i] = 能做出这题的人数 vector<int> c; for (int x : b) { if (x > kevin) { // 只有 Kevin 做不出的题才会影响排名 int cnt = others.end() - lower_bound(others.begin(), others.end(), x); c.push_back(cnt); } } sort(c.begin(), c.end()); int sz = c.size(); vector<long long> ans(m + 2, 0); for (int k = 1; k <= m; k++) { int t = sz / k; // 能分 t 组 long long sum = 0; for (int i = 1; i <= t; i++) { sum += c[i * k - 1] + 1; } ans[k] = sum; } for (int k = 1; k <= m; k++) { cout << ans[k] << " "; } cout << "\n"; } int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }四、代码分步解析
-
输入处理 读入多组测试用例,每组读入参赛人数 、题目数 ,以及数组 。
-
筛选强于 Kevin 的选手 取出 作为 Kevin 评分,只保留评分严格更大的选手,并排序,方便后续二分查找。
-
构建代价数组 遍历所有题目,只保留难度大于 Kevin 的题目; 用
lower_bound二分统计能做出该题的高分选手数量,存入 。 -
贪心计算每个 的答案 将 升序排序; 对每个 ,可以分成 组,每组取第 位置元素为组内最大值,累加 作为排名总和。
-
输出答案 依次输出 对应的最小排名和。
五、复杂度分析
排序:; 二分统计:; 枚举所有 求和为调和级数复杂度 ; 整体复杂度满足题目 数据限制。
六、算法关键点
- 剔除对排名无影响的弱选手和简单题目;
- 利用二分快速统计每题影响人数;
- 排序后连续分组贪心,保证每组最大值最小,从而总和最小。
- 1
信息
- ID
- 7038
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者