9.10 模拟赛(炼石计划 11 月 15日 CSP-S 十连测 #10)

炼石计划 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\) 一个升序排序一个降序排序即可。

posted @ 2024-09-10 15:28  2huk  阅读(59)  评论(0编辑  收藏  举报