炼石计划 11 月 15日 CSP-S 十连测 #10【补题】 - 比赛 - 梦熊联盟 (mna.wang)
复盘
所有题先都浏览了一遍。其中 T1 见过。但当时是乱搞过的。但怎么乱搞的忘了。
那就先做 T1。有 \(60\) 分送的。尝试重新思考乱搞以获取剩余的 \(40\) 分。
中间看了一眼 T3。想了一分钟左右就会做了。这是一个很多套路集合的题。于是先放 T1 写 T3。
代码很短(40 行)。调了一小会就过了样例。时间还早(比赛刚进行不到一小时)于是先写对拍。
发现一个好东西:windows 可以切换桌面,你可以把对拍程序放桌面 2,其它放桌面 1。这样在你写别的程序时就不会被正在跑的 dp.bat 干扰了。尽管这不重要。
欸 T2 好像也见过类似的。反正做法一定是贪心,但贪心策略不明。部分分里有 \(24\) 分爆搜,先写这个。
有个 \(8\) 分的 \(n = 10^2,m=10^5,c\le 10^4\)。这是什么???分这么少的部分分我不会???反正是 \(8\) 分不要也罢。
尝试思考了一会儿正解。其弱化版 \(c_i = 1\) 是经典题,Acwing 算法基础课的最后几节有讲。那题的做法是按右端点排序然后能选就选。这两道题有关系吗?
不清楚。但如果延用这题的思路应该能骗不少分。于是写了一颗线段树。此时我的心态是保底 \(24\) 上限无限,但没不觉得能满分。
T4 一眼不会。写了送的 \(10\) 分就跑路了。
主线任务思考 T1 乱搞还没完成呢!最终也得到了一个需要用线段树维护的乱搞做法。但是对拍正确的概率很低很低很低。
值得一提的是,写 T1 送的 \(60\) 分时差点给我送了。险些 \(100\to40\)。
预计 \(60+ \operatorname{eps}, [24, 100), 100, 10\)。实际 \(100,100,100,10\)???T1 数据这么水???
T3 正解 Trie???我的思路不更简单???
总结
好的:
- 运气无敌。
不足:
- T1 的暴力写了很长时间。写代码前要想清楚较为简介的实现方法不要上来就是一个线段树。
- 给 T1 的乱搞做法写对拍时浪费了较长时间。
知识点
T1:暴力(?)优化
T2:贪心
T3:前缀和,位运算(Trie 树)
T4:贪心
题解
A. 体育课2
简洁题意:有 \(n\) 个人排成一排,每个人有一个权值构成了一个排列。每次对于每个人而言,如果他右面的第一个还活着的人的数小于自己的数,就把他杀了。求经过多少轮后没有人会死。
令 \(cnt_i\) 表示第 \(i\) 个人杀了多少人。那么答案为 \(\max cnt_i\)。考虑求解 \(cnt\)。
对于排列 \(3, 1, 2\),显然 \(2\) 被杀之前 \(1\) 会被杀。或者说只有 \(1\) 被杀了 \(2\) 才会被杀。这很像一个 flood fill 的过程。考虑队列维护。(或者认为是 bfs。)
在此同时维护链表,这有助于我们快速求出某个人后面第一个没死的人。
然后模拟即可。复杂度 \(\mathcal O(n)\)。
可能代码更有助于理解:提交记录 #610619 - 梦熊联盟 (mna.wang)
B. 零件加工
简洁题意:数轴上有 \(n\) 个数和 \(m\) 个区间。你需要选择尽量多的区间数量,使得每个点被覆盖的区间数量 \(\le c_i\)。
\(c_i = 1\) 是 908. 最大不相交区间数量 - AcWing题库。这两题做法相同,将所有区间按照右端点升序排序,然后只要合法就添加。
贪心证明只会感性。
C. 优美的序列
简洁题意:求有多少区间异或和 \(\ge k\)。
套路求解前缀和 \(s_i = a_1 \operatorname{xor} \dots a_i\)。那么 \((l, r)\) 需要使得 \(s_r \operatorname{xor} s_{l-1} \ge k\)。
显然的也是 std 的做法是 Trie 树。但我没有。
运用类似数位 DP 的思想。在二进制视角下,如果 \(a > b\),那么必然存在一个前缀使得 \(a, b\) 相同,且这个前缀的下一位满足 \(a\) 是 \(1\) 且 \(b\) 是 \(0\),后面无限制。
我们枚举 \(s_r \operatorname{xor} s_{l-1}\) 与 \(k\) 相同的前缀长度。因为后面的位无限制所以直接将 \(k, a_1\dots a_n\) 都右移这个长度。
得到新的 \(k',a'_1\dots a'_n,s'_1\dots s'_n\) 后,问题转化成了:
- 求有多少 \((l, r)\) 使得 \(s'_r \operatorname{xor} s'_{l-1} = k'\)。
这是平凡的。
因为用了 map 所以总复杂度两个 \(\log\)。当然也可以用 umap。提交记录 #610551 - 梦熊联盟 (mna.wang)
D. 产品加工
考虑如果只有一道工序该怎么做。
考虑贪心。我们每次找到当前仍空闲的且 \(t\) 最小的机器操作即可。
实现上你可以维护一个堆。这份代码里 pair 的 first 维护的信息是:
- 如果某件商品要用这个机器,那么这件商品将会在什么时刻完成制作。也就是在原来 \(t\) 的基础上加了排队的时长。
void solve(int *f, int *a, int n) { // f[i] 表示第 i 件物品在什么时候制作完成
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> Q;
for (int i = 1; i <= n; ++ i ) Q.push({a[i], i});
for (int i = 1; i <= q; ++ i ) {
int x = Q.top().first, y = Q.top().second;
Q.pop();
f[i] = x;
Q.push({x + a[y], y});
}
}
两道工序?
我们可以将两道工序分别跑一遍上面的流程。得到两个长度为 \(q\) 的数组 \(f, g\)。
但是 \(f, g\) 的内部顺序是未定的。这是因为我们在求解时将所有商品都视作相同,但是两道工序拼起来后这样考虑就不对了。
对于一个物品而言,它所花的时间是 \(f_i + g_i\)。所以总流程花的时间是 \(\max(f_i + g_i)\)。我们要最小化 \(\max(f_i + g_i)\)。
将 \(f, g\) 重排,使得 \(\max(f_i + g_i)\) 最小是经典问题。\(f, g\) 一个升序排序一个降序排序即可。
标签:11,10,15,乱搞,100,T1,operatorname,贪心 From: https://www.cnblogs.com/2huk/p/18406465