Kevin and Competition Memories
思路
题意还是比较清楚的
考虑当 \(k\) 确定时, 怎么计算 \(\rm{Kevin}\) 的最优排名之和
手动模拟一下样例, 这个题应该要以人为基准, 然后再看题目难度对于 \(\rm{rank}\) 的影响
首先我们思考一下, 对于一个能力值序列 \(a\) , 插入题目怎样影响了 \(\rm{Kevin}\) 的排名
对于一个题目, 他的难度可以使得 \(k\) 个人做出来这个题并且 \(\rm{Kevin}\) 没法做出来, 那么他对 \(\rm{Kevin}\) 排名的影响就是 \(k\) , 这是显然的, 如果 \(\rm{Kevin}\) 做出来了那么就没有影响
同时, 你发现对于多道题, 他们对 \(\rm{Kevin}\) 排名的影响仅仅是单独每道题的影响取 \(\max\)
那么策略就很明显了, 我们对于每个题目的影响, 一定是让小的与小的组合, 大的与大的组合, 处理影响是 \(\mathcal{O} (n \log n)\) 的, 然后对于排序后的题目, 选择即可
时间复杂度 \(\mathcal{O} (n \log n + \frac{m}{1} + \frac{m}{2} + \cdots + \frac{m}{m}) = \mathcal{O} (n \log n + m \log m)\)
考虑复习
- 定义操作 (约束) 和开销 / 收益, 要求最值化开销 / 收益
- 推式子计算约束条件
- 模拟操作情况, 找到最好开销, 注意最大和最小, 一般来说可以贪贪心 (将简单情况先处理, 然后在基础上处理最值)
- 考虑操作对答案的影响 (推式子) , 据此对操作进行处理
- 推导每个元素对答案的贡献
- 推导动态规划
- 判断是否存在操作满足约束
- 模拟, 对于重复情况去重
- 要求一个数列的多个部分 \((\)前缀, 子串 \(\cdots\)\()\) 的成本
- 贪心找到最优操作的构造方法, 加上优化 / 找共同点
题意
个人, 每个人有数值 , 道题目, 每道题目有数值
如果一个人的数值 题目数值, 那么他可以做这道题目
一个人的排名定义为那些比他解掉更多题目的参赛者数量加一
对于
要求你求出:
如果一场比赛有 道题, 准许你放弃 道题, 那么在 场比赛中, 第一个人最小的排名之和为多少
先考虑对于一个确定的 \(k\) , 如何处理最优解\((\)找到最好开销\()\)
首先把所有第 \(1\) 个人做得出来的题和数值低于第 \(1\) 个人的人甩掉, 没有影响
剩下的题, 计算出有多少个人做得出来, 记为 \(p_i\)
考虑单个元素对答案的影响, 也即一个题目会对排名有多少影响
对于一场比赛, 假设其题目集合为 \(\mathbb{S} = \{p_1, p_2, \dots, p_k\}\) , 那么排名应该会是 \(\max\limits_{i=1}^{k} p_i\)
为什么?
对于题目权值 \(p_i\) , 其操作就是对于 \(1 \sim p_i\) 的人, 做题数加 \(1\), 那么显然只有最大的一次是贡献
知道了这个, 我们考虑贪心放置题目
从小到大放置分组即可
实现
框架
先双指针处理题目代价, 然后再随便处理即可
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 3e5 + 20;
int n, m;
int Abi[MAXN], Mar[MAXN];
int Kmar, breakpoint;
int Cost[MAXN];
/*预处理所有问题的代价*/
void init() {
for (int i = 1; i <= m; i++) Cost[i] = 0;
int cnt = 0;
for (int i = 1, pointer = breakpoint; i <= m && pointer <= n; i++) {
if (Mar[i] <= Kmar) continue;
while (Abi[pointer] < Mar[i] && pointer <= n) pointer++, cnt++;
Cost[i] = n - cnt - breakpoint + 1;
}
std::sort(Cost + 1, Cost + m + 1);
}
signed main()
{
int T; scanf("%lld", &T);
while (T--) {
scanf("%lld %lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &Abi[i]);
for (int i = 1; i <= m; i++) scanf("%lld", &Mar[i]);
Kmar = Abi[1];
std::sort(Abi + 1, Abi + n + 1), std::sort(Mar + 1, Mar + m + 1);
for (int i = 1; i <= n; i++)
if (Abi[i] > Kmar) { breakpoint = i; break; }
init();
for (int k = 1; k <= m; k++) {
int Ans = 0;
for (int i = k; i <= m; i += k) {
Ans += 1 + Cost[i];
}
printf("%lld ", Ans);
}
printf("\n");
}
return 0;
}
总结
善于从样例来找到性质
一般来说, 先去除无效数据是有用的

浙公网安备 33010602011771号