Codeforces Round #824 (Div. 2) A-E
A
题解
知识点:贪心,数学。
注意到三段工作时间一共 天,且天数实际上可以随意分配到任意一段,每段至少有一天,现在目的就是最大化段差最小值。
不妨设 ,显然满足等式 时段差相等,即没有长度被浪费。
解得 ,于是 ,即 ,现在考虑余数部分分配。显然,多余的一天或者两天分配到 不会影响段差最小值,但分配到 只可能变小,所以 只能是 ,余数全扔进 但对答案没影响,因此之后不考虑余数。
为使 最大,显然设 ,因此最终答案是
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int n; cin >> n; cout << (n - 3) / 3 - 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; }
B
题解
知识点:贪心。
确定最小值 ,把大于等于 的数拆分了即可,且不会影响最小值。
一个数拆分的最小次数是 ,方法是每次把 拆出来,能拆 次,但最后一次拆会导致剩下的小于 ,所以最后一次可以替换成对半拆,但不影响总次数。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[107]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; int mi = *min_element(a + 1, a + 1 + n); mi = mi * 2 - 1; ll sum = 0; for (int i = 1;i <= n;i++) { if (a[i] > mi) sum += (a[i] + mi - 1) / mi - 1; } cout << sum << '\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 vis[26]; int pst[26]; bool check(int a, int b) { if (vis[b] || a == b) return false; int k = b, cnt = 0; while (~pst[k]) { k = pst[k]; cnt++; } return k != a || cnt == 25; } bool solve() { memset(vis, 0, sizeof(vis)); memset(pst, -1, sizeof(pst)); int n; cin >> n; string s; cin >> s; string ans; for (int i = 0;i < s.size();i++) { if (~pst[s[i] - 'a']) { ans += pst[s[i] - 'a'] + 'a'; continue; } for (int j = 0;j < 26;j++) { if (check(s[i] - 'a', j)) { vis[j] = 1; pst[s[i] - 'a'] = j; ans += j + 'a'; break; } } } 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; }
D
题解
知识点:枚举,数学,STL。
配成 set
的卡牌各个特征和都能被 整除,即 ,表示能组成 set
的 三张牌的任意特征 的情况。
注意到卡牌两两不同,因此通过两张卡牌就能唯一确定另一张可以配对成 set
的卡牌的牌型,即 ,表示通过 两张牌能确定 的牌型,这也意味着不同的 set
至少有两张牌不同。
想要组成一个 meta-set
,需要五张牌组成至少两个 set
,但根据上面的结论,两个 set
至多有一个重复的牌,就已经有五张牌了。因此一个 meta-set
有五张牌,需要且只需要组成两个 set
,且其中一张牌会同时存在于两个 set
。
因此我们先遍历所有两张牌的组合(称为一对牌),得到能够和这一对牌组成 set
的牌型,并在这种牌型下计数,表示这种牌型可以和几对牌组成 set
。假如某种牌型下存在 对牌,则可以从中选 对组成 meta-set
,总计有 种方案。
最后遍历一遍牌,并通过之前记录的牌型与对数,计算方案总数,根据上面结论,这些方案不可能交叉。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int n, k; cin >> n >> k; vector<vector<int>> c(n + 1, vector<int>(k)); for (int i = 1;i <= n;i++) for (int j = 0;j < k;j++) cin >> c[i][j]; map<vector<int>, int> mp; for (int i = 1;i <= n;i++) { for (int j = i + 1;j <= n;j++) { vector<int> v(k); for (int l = 0;l < k;l++) v[l] = (6 - c[i][l] - c[j][l]) % 3;//找到与两张卡牌配对的第三张卡牌 mp[v]++; } } int ans = 0; for (int i = 1;i <= n;i++) { int tmp = mp[c[i]]; ans += (tmp - 1) * tmp / 2; } 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; }
E
题解
知识点:枚举,STL。
因为 到 的距离是打乱的,可以通过枚举 之间的可能的间距,检查这个距离 是否合法,从而推断出可能序列。
先固定一个端点的距离 ,匹配另一个端点距离 ,这样就能枚举到所有情况。此时有两种情况,第一种是点在 之间,即 ,第二种是点在 之外,即 ,两种都枚举一下即可。
先假设 。
在判断一个 是否合法时,先把距离 大于 的处理掉,即找到当前 中最大的且大于 的,这种情况的点只可能在 之外。假设 在 中, 一定存在于 中,否则就是 不合法。存在的话就把 从 删掉, 就是 之一。
之后没有在 之外的点时,剩下的点都在内部,则把当前 从小到大排序, 从大到小排序,其对应位置之和都应该等于 ,否则就不合法。
最后要求输出非负数,所以把整个序列偏移一下即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int n; int d1[1007], d2[1007]; struct node { multiset<int> ms; void insert(int x) { ms.insert(x); } int max() { return *prev(ms.end()); } int size() { return ms.size(); } void erase(int x) { ms.erase(ms.find(x)); } bool exist(int x) { return ms.find(x) != ms.end(); } }; bool check(int dis) { node ms1, ms2; for (int i = 1;i <= n;i++) ms1.insert(d1[i]), ms2.insert(d2[i]); int p1 = 0, p2 = dis; vector<int> ans; while (ms1.size() && ms2.size() && max(ms1.max(), ms2.max()) >= dis) {//先清理在假定的p1,p2外的点 if (ms1.max() > ms2.max()) {//|p1-h|最长 int x = ms1.max(); int diff = x - dis; if (!ms2.exist(diff)) return false; ms1.erase(ms1.max()); ms2.erase(diff); ans.push_back(p1 + x); } else {//|p2-h|最长 int x = ms2.max(); int diff = x - dis; if (!ms1.exist(diff)) return false; ms1.erase(diff); ms2.erase(ms2.max()); ans.push_back(p2 - x); } } vector<int> v1, v2; for (auto i : ms1.ms) v1.push_back(i); for (auto i : ms2.ms) v2.push_back(i); reverse(v2.begin(), v2.end()); for (int i = 0;i < v1.size();i++) {//其他点都在内部 if (v1[i] + v2[i] != dis) return false; } for (auto i : v1) ans.push_back(i); int diff = max(-*min_element(ans.begin(), ans.end()), 0);//保证最小坐标是非负的 cout << "YES" << '\n'; for (auto i : ans) cout << i + diff << ' '; cout << '\n'; cout << diff << ' ' << dis + diff << '\n'; return true; } bool solve() { cin >> n; for (int i = 1;i <= n;i++) cin >> d1[i]; for (int i = 1;i <= n;i++) cin >> d2[i]; for (int i = 1;i <= n;i++) { if (check(abs(d1[1] - d2[i]))) return true; if (check(d1[1] + d2[i])) return true; } return false; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << "NO" << '\n'; } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16767592.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)