Codeforces Round #823 (Div. 2) A-D
A
题解
知识点:贪心。
对于一个轨道,要么一次性清理,要么一个一个清理。显然,如果行星个数大于直接清理的花费,那么选择直接清理,否则一个一个清理。即 ,其中 表示轨道 的行星个数。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int cnt[107]; bool solve() { int n, c; cin >> n >> c; memset(cnt, 0, sizeof(cnt)); for (int i = 1;i <= n;i++) { int x; cin >> x; cnt[x]++; } int ans = 0; for (int i = 1;i <= 100;i++) ans += min(c, cnt[i]); 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; }
B
题解
方法一
知识点:三分。
按位置从小到大排列,显然约会花费是一个关于 的单谷函数,因此可以三分位置。
由于位置最大有 ,但点的个数只有 ,考虑先用 map
存储有序对 ,其中 是位置 的人最大打扮时间,因为比这个时间少的一定不影响结果。遍历结束以后把 map
内容移到 vector
中用 pair
存储用以三分,check
函数则只要遍历一遍 vector
即可。
时间复杂度
空间复杂度
方法二
知识点:贪心。
把 等效进位置,如果 在 左侧,则等效位置是 ;如果 在 右侧,则等效位置是 。
所有点的左侧等效位置最左的位置,就是等效区间左端点;所有点的右侧等效位置最右的位置就是等效区间的右端点。
如果等效区间的左右端点来自于不同两点的等效点,那么等效区间的中点一定在这两点之间,否则原来的点必有一个能覆盖另一个点,等效区间的左右端点就属于同一个点的等效点。
时间复杂度
空间复杂度
代码
方法一
#include <bits/stdc++.h> #define ll long long using namespace std; int x[100007]; map<int, int> mp; vector<pair<int, int>> v; double check(double mid) { double mx = 0; for (auto [i, j] : v) { mx = max(mx, abs(i - mid) + j); } return mx; } bool solve() { mp.clear(); v.clear(); int n; cin >> n; for (int i = 1;i <= n;i++) { cin >> x[i]; mp[x[i]] = 0; } for (int i = 1;i <= n;i++) { int T; cin >> T; mp[x[i]] = max(mp[x[i]], T); } for (auto [i, j] : mp) { v.push_back({ i,j }); } double l = 0, r = v.back().first; while (abs(r - l) >= 1e-7) { double mid1 = l + (r - l) / 3; double mid2 = r - (r - l) / 3; if (check(mid1) <= check(mid2)) r = mid2; else l = mid1; } cout << fixed << setprecision(10) << l << '\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; }
方法二
#include <bits/stdc++.h> #define ll long long using namespace std; int x[100007], T[100007]; bool solve() { int n; cin >> n; int l = 1e9, r = 0; for (int i = 1;i <= n;i++) cin >> x[i]; for (int i = 1;i <= n;i++) cin >> T[i]; for (int i = 1;i <= n;i++) { l = min(x[i] - T[i], l);///最左侧等效点 r = max(x[i] + T[i], r);///最右侧等效点 } cout << fixed << setprecision(8) << (l + r) / 2.0 << '\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
题解
知识点:贪心。
因为要字典序最小,那么一个数字他后面没有更小的数字则可以保留,其他都应该删除,所以从右往左找一个合法的保留序列,其他的数字加一,并且都是位置随意的,于是可以插入到保留下来的序列,并使插入后的序列是从小到大字典序最小的排列。因此直接把保留序列外的数字加一以后,对整个序列排序即可。
也可以直接桶排序。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { string s; cin >> s; int mi = 10; for (int i = s.size() - 1;i >= 0;i--) { if (s[i] - '0' <= mi) mi = s[i] - '0'; else s[i] = min(s[i] + 1, '9' + 0); } sort(s.begin(), s.end()); cout << s << '\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
题解
知识点:构造。
注意到操作不会改变无序对 数量以及种类。
引理: ,当且仅当无序对是回文的。
充分性:
当 时,如果 处存在一组无序对 ,则必然会在 产生相同一组无序对 ,除非当 为奇数时,可以在中间产生一个元素相同的无序对 ,因此 时,无序对必然成回文状。
必要性:
当无序对是回文的,则第 组无序对 可以对应第 组无序对 ,即 ,所以 。
充要条件:YES 当且仅当无序对 中元素不同的无序对有偶数个,元素相同的无序对仅在 为奇数时至多 种有奇数个。
充分性:
根据引理,显然满足右边条件。
必要性:
显然没有任何限制时,给出的无序对条件能排列成回文的,现在尝试证明其必然可构造无序对回文。
注意到操作 可以使得 和 交换位置,即 这一组无序对被置换到了 号位置,同时 这一组无序对被置换到了 号位置,但这不会改变 和 的顺序,即第 到 组无序对及其实际元素顺序没有改变。因此,如果我们想要将无序对通过操作变成一个我们想要的顺序,可以从右往左构造。
假设 到 的无序对都安排好了,现在 号位置想要 号位置的无序对时,可以先 ,将 号替换到 号,然后 ,将 号替换 号,过程中 的无序对不会改变,包括实际元素顺序。
上述操作最后结果是无序对 替换到 ,且 号无序对元素的实际顺序不会改变。但如果我们希望实际元素的顺序也发生改变,我们可以加一个步骤 在中间,即通过 替换 号后的 号元素实际顺序与原来是相反的,这也是为什么我们只需要知道无序对顺序即可,因为元素实际顺序是可以随时改变的。
通过上述操作我们可以实现无序对的任意排列,以及无序对实际元素的顺序。因此无序对满足回文条件时,必然可以构造出无序对回文。于是根据引理,得到 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; string a, b; int cnt[26][26]; bool solve() { memset(cnt, 0, sizeof(cnt)); int n; cin >> n; string a, b; cin >> a >> b; for (int i = 0;i < n;i++) { int x = a[i] - 'a', y = b[n - 1 - i] - 'a'; if (x > y) swap(x, y); cnt[x][y]++; } bool ok = true; int esum = 0; for (int i = 0;i < 26;i++) { for (int j = i;j < 26;j++) { if (i == j) esum += cnt[i][j] & 1; else ok &= !(cnt[i][j] & 1); } } if (ok && esum <= (n & 1)) cout << "YES" << '\n'; else cout << "NO" << '\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/16753089.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧