Educational Codeforces Round 142 (Rated for Div. 2) A-D
A
题意
给定 个怪物的血量 ,每次可以选择两种操作之一:
- 选择一个怪物直接杀死。
- 选择两个怪物血量减一(怪物血量为 视作死亡)。
问最少多少次操作可以消灭所有怪物。
题解
知识点:贪心。
存在一对血量为 的怪物选择操作2最好,否则选择操作1。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int n; cin >> n; int cnt = 0; for (int i = 1;i <= n;i++) { int x; cin >> x; if (x == 1) cnt++; } int rst = n - cnt / 2 * 2; cout << rst + cnt / 2 << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
B
题意
两个观众A和B的初始心情都为 。
给定四个数 表示四种笑话的个数, 能使得心情都加 , 能使A心情加 B心情减 , 能使B心情加 A心情减 , 能使心情都减 。任意一个观众的心情变为负数,则停止。
问最多能讲几个笑话。
题解
知识点:贪心。
分两类情况:
- ,最多只能操作一次。
- ,可以操作 次。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int a, b, c, d; cin >> a >> b >> c >> d; if (a == 0) { cout << min(b + c + d, 1) << '\n'; return 1; } else { int ans = a + 2 * min(b, c) + min(a, abs(b - c) + d) + (abs(b - c) + d > a); cout << ans << '\n'; } return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
C
题意
给一个长为 的排列,每次操作可以任选两个数,其中小的挪到开头,大的挪到末尾,问最少几次操作可以使得排列有序。
题解
知识点:贪心,枚举,双指针。
注意到,操作不影响没有被操作过的数字的相对位置,因此考虑排列中不需要操作的数字。显然,最终被保留的数字应该是连续上升的一个子序列,如 23456
是,而 13456
不是因为 和 中间没有 。
假设我们操作了某一组数 ,那么 一定都需要操作一遍才能保证这些数字有序。因此只有中间的数我们不需要操作,所以我们保留的数字应该从中间开始往外拓展。
若 为奇数,则从中点 开始往两边扩展;若 为偶数,先保证 有序,再从这两个数两边扩展,如果不有序直接输出 。
为了方便找到某个数的位置,我们可以先处理数到位置的映射 ,再利用双指针 指向扩展的边界,向两边同时扩展,如果有一边扩展不了那就不需要继续了,最后结果是 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int pos[200007]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) { int x; cin >> x; pos[x] = i; } int l = (1 + n) / 2, r = (1 + n + 1) / 2; if (pos[l] > pos[r]) { cout << n / 2 << '\n'; return 1; } while (1 < l && r < n) { if (pos[l - 1] > pos[l] || pos[r] > pos[r + 1]) break; l--; r++; } cout << l - 1 << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
D
题意
给你 个长为 的排列 。
定义一个排列 的值为满足 中 的最大值。
定义两个排列 的乘法 为 。
对于给定的 个排列中,对于每个排列 找到另一个排列 ( 可以等于 )使得 的值最大,求出这 个最大值。
题解
知识点:枚举。
先考虑两个排列 乘积的求值过程,即如何求出 中 最大值。
显然 ,即 中 的位置是 ,我们就能得到 至少是 ,以此类推直到 就能得到 ,复杂度是 ,先考虑先优化枚举过程。
既然我们要知道某个数的位置,那么我们可以先预处理出 所有数字出现的位置 。我们发现 等价于 ,即 出现的位置等于 那么自然可以得到 ,由此我们从 开始找到最大的 满足 即可。现在复杂度是 ,考虑优化 次查找。
我们发现查找的过程,其实就是一个 和 个 匹配最长前缀的过程,可以用字典树 trie
解决,复杂度是 。但这里 不大(其实是我不会字典树),我们可以将排列用十进制压缩成一个整数,用 map
记录 个排列的前缀信息来解决。设 为 个排列的 前 个数的前缀信息,例如排列 前三个数字的信息就是 ,记录在 中。例如,我们查找 前 个数的匹配信息时,只要判断 中有无 即可。到此为止,我们对 从前 个数依次查找,最多查找 次就可以找到最大的 了,复杂度是 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; int a[50007][11]; int pos[11]; map<ll, int> mp[11]; bool solve() { int n, m; cin >> n >> m; for (int i = 1;i <= m;i++) mp[i].clear(); for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { cin >> a[i][j]; pos[a[i][j]] = j; } ll _t = 0; for (int j = 1;j <= m;j++) { _t = _t * 10 + pos[j] - 1; mp[j][_t] = 1; } } for (int i = 1;i <= n;i++) { ll _t = 0; int ans = m; for (int j = 1;j <= m;j++) { _t = _t * 10 + a[i][j] - 1; if (!mp[j][_t]) { ans = j - 1; break; } } cout << ans << ' '; } cout << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17067292.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧