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

模拟。

a1an 的调整的代价是固定的,即为 1in|aibi|。在对它们调整的过程中需要选择一个数复制一份变为 an+1,并最小化调整 an+1bn+1 的代价 |bn+1an+1|

若存在 1in,使得 min(ai,bi)bn+1max(ai,bi),则 |bn+1an+1| 的值可以为 0;否则选择的被复制的数一定在集合 {ai}{bi} 中,选择其中与 bn+1 最接近的数复制并计算代价即可。

//
/*
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

排序。

读错题了唐唐唐唐唐——获得了一道新题哈哈,什么时候需要就放上去。

首先假设 n+m+1 个人全部到来,按照题意进行模拟找到其较优岗位并求得贡献之和。

然后按照到来顺序,分别枚举每个岗位中的人 i,记该岗位人数上限为 k

  • 若这个人之前已经有 k 个人入职该岗位,则这个人不来贡献仅减少 min(ai,bi)
  • 若这个人之前不到 k 个人入职该岗位,且以该岗位为较优岗位的人总数不大于 k 个,则这个人不来贡献减少 max(ai,bi)
  • 否则这个不来一定会使得第 k 个人入职该岗位,则贡献变化量为 max(ai,bi)+max(ak,bk)min(ak,bk)

讨论即可,总时间复杂度 O(n+m) 级别。

//
/*
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。

首先考虑如果选出了一段区间 [l,r] 并将其逐字符取反,如何判断取反后整个括号序列是否合法。一个显然的想法是将 [1,l1][l,r][r+1,n] 三段括号串分别先进行括号匹配将它们化简,然后检查化简后的三段能否匹配。套路地考虑将左右括号分别看做 ±1,以 (()())(()) 为例则有如下的折线图:

图片来自:https://www.cnblogs.com/extractstars/p/18223651

发现区间 [l,r] 取反相当于将选中的折线 (l1)r 中各段进行翻折。为了保证翻折后折线仍然是连续的,且不会翻折到 x 轴下方,记位置 i 处折线高度为 hi,则需要满足:

hl1=hrmaxlir1hi2×hl1

上述 h 数组可以在括号匹配过程中直接求得,则一个显然的想法是按照值递减枚举 hl1=hr,再顺序枚举对应的每个位置,然后考虑每个位置与之前多少位置可以构成合法的修改区间。发现仅需检查其中是否有不满足条件 2 的位置即可,考虑双指针枚举 2×hl1 的所有位置并使用 set 记录,求合法位置对时同样双指针求得满足条件 2 的位置的合法对即可。

总时间复杂度 O(nlogn) 级别。

//
/*
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:括号序列的又一种数形结合的模型转换。
posted @   Luckyblock  阅读(271)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示