Educational Codeforces Round 166 (Rated for Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/1976
满课,并且 48 小时之内只睡了 8h。
本来不想打的,但是手痒就上小号打了,然而唐唐唐掉大分呃呃
A
签到。
感谢 isdigit
函数。
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= //============================================================= //============================================================= int main() { //freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { int n; std::cin >> n; std::string s; std::cin >> s; int flag = 1; for (int i = 1; i < n; ++ i) { if (!isdigit(s[i - 1]) && isdigit(s[i])) flag = 0; } for (int i = 0, lst = -1; i < n; ++ i) { if (isdigit(s[i])) { if (lst == -1) lst = i; else if (s[lst] > s[i]) flag = 0; lst = i; } } for (int i = 0, lst = -1; i < n; ++ i) { if (!isdigit(s[i])) { if (lst == -1) lst = i; else if (s[lst] > s[i]) flag = 0; lst = i; } } std::cout << (flag ? "YES\n" : "NO\n"); } return 0; }
B
模拟。
对 的调整的代价是固定的,即为 。在对它们调整的过程中需要选择一个数复制一份变为 ,并最小化调整 至 的代价 。
若存在 ,使得 ,则 的值可以为 0;否则选择的被复制的数一定在集合 中,选择其中与 最接近的数复制并计算代价即可。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2e5 + 10; //============================================================= int n, a[kN], b[kN]; //============================================================= //============================================================= int main() { //freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n; for (int i = 1; i <= n; ++ i) std::cin >> a[i]; for (int i = 1; i <= n + 1; ++ i) std::cin >> b[i]; LL ans = 0, flag = 0; for (int i = 1; i <= n; ++ i) { ans += abs(a[i] - b[i]); if (std::min(a[i], b[i]) <= b[n + 1] && b[n + 1] <= std::max(a[i], b[i])) flag = 1; } if (!flag) { LL delta = 1e9; for (int i = 1; i <= n; ++ i) { delta = std::min(delta, 1ll * abs(b[n + 1] - a[i])); delta = std::min(delta, 1ll * abs(b[n + 1] - b[i])); } ans += delta; } std::cout << ans + 1 << "\n"; } return 0; }
C
排序。
读错题了唐唐唐唐唐——获得了一道新题哈哈,什么时候需要就放上去。
首先假设 个人全部到来,按照题意进行模拟找到其较优岗位并求得贡献之和。
然后按照到来顺序,分别枚举每个岗位中的人 ,记该岗位人数上限为 :
- 若这个人之前已经有 个人入职该岗位,则这个人不来贡献仅减少 。
- 若这个人之前不到 个人入职该岗位,且以该岗位为较优岗位的人总数不大于 个,则这个人不来贡献减少 。
- 否则这个不来一定会使得第 个人入职该岗位,则贡献变化量为 。
讨论即可,总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long #define pii std::pair<int,int> #define mp std::make_pair const int kN = 2e5 + 10; //============================================================= int n, m; LL sum, ans[kN]; std::vector<int> aa, bb; int a[kN], b[kN], c[kN]; //============================================================= //============================================================= int main() { // freopen("1.txt", "r", stdin); std::ios::sync_with_stdio(0), std::cin.tie(0); int T; std::cin >> T; while (T --) { std::cin >> n >> m; sum = 0; aa.clear(), bb.clear(); for (int i = 1; i <= n + m + 1; ++ i) std::cin >> a[i]; for (int i = 1; i <= n + m + 1; ++ i) { std::cin >> b[i]; c[i] = abs(a[i] - b[i]); sum += std::min(a[i], b[i]); } int cnta = 0, cntb = 0; for (int i = 1; i <= n + m + 1; ++ i) { if (a[i] > b[i]) { ++ cnta; if (cnta <= n) sum += a[i] - b[i]; aa.push_back(i); } else { ++ cntb; if (cntb <= m) sum += b[i] - a[i]; bb.push_back(i); } } for (int i = aa.size() - 1; i >= n; -- i) ans[aa[i]] = sum - b[aa[i]]; if ((int) aa.size() <= n) { for (int i = 0; i < (int) aa.size(); ++ i) ans[aa[i]] = sum - a[aa[i]]; } else { for (int i = n - 1; i >= 0; -- i) ans[aa[i]] = sum - a[aa[i]] + c[aa[n]]; } for (int i = bb.size() - 1; i >= m; -- i) ans[bb[i]] = sum - a[bb[i]]; if ((int) bb.size() <= m) { for (int i = 0; i < (int) bb.size(); ++ i) ans[bb[i]] = sum - b[bb[i]]; } else { for (int i = m - 1; i >= 0; -- i) ans[bb[i]] = sum - b[bb[i]] + c[bb[m]]; } for (int i = 1; i <= n + m + 1; ++ i) std::cout << ans[i] << " "; std::cout << "\n"; } return 0; }
D
括号序列,枚举。
好玩题,场上一直想着怎么拆成两半拼起来了,还能这么想的 oonp。
首先考虑如果选出了一段区间 并将其逐字符取反,如何判断取反后整个括号序列是否合法。一个显然的想法是将 ,, 三段括号串分别先进行括号匹配将它们化简,然后检查化简后的三段能否匹配。套路地考虑将左右括号分别看做 ,以 (()())(())
为例则有如下的折线图:

发现区间 取反相当于将选中的折线 中各段进行翻折。为了保证翻折后折线仍然是连续的,且不会翻折到 轴下方,记位置 处折线高度为 ,则需要满足:
上述 数组可以在括号匹配过程中直接求得,则一个显然的想法是按照值递减枚举 ,再顺序枚举对应的每个位置,然后考虑每个位置与之前多少位置可以构成合法的修改区间。发现仅需检查其中是否有不满足条件 2 的位置即可,考虑双指针枚举 的所有位置并使用 set
记录,求合法位置对时同样双指针求得满足条件 2 的位置的合法对即可。
总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 2e5 + 10; int n, pre[kN]; std::string s; LL ans; std::vector<int> pos[kN]; std::set<int> high; void solve() { n = s.length(); high.clear(); for (int i = 0; i < n; ++ i) { pre[i + 1] = pre[i] + (s[i] == '(' ? 1 : -1); pos[i + 1].clear(); } for (int i = 1; i <= n; ++ i) pos[pre[i]].push_back(i); for (int i = n, j = n; i; -- i) { while (j > 2 * i) { for (auto p: pos[j]) high.insert(p); -- j; } for (int k = 1, sz = pos[i].size(), cnt = 1; k < sz; ++ k) { auto it = high.upper_bound(pos[i][k - 1]); if (it == high.end() || *it > pos[i][k]) { ans += cnt; } else { cnt = 0; } ++ cnt; } } } int main() { int T; std::cin >> T; while (T --) { std::cin >> s; ans = 0; solve(); std::cout << ans << "\n"; } } /* 1 ()()()() 1 ()((())) 10123210 (()(())) ()(()()) */
E
写在最后
参考:
学到了什么:
- D:括号序列的又一种数形结合的模型转换。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧