AtCoder Beginner Contest 384
省流版
- A. 模拟转换即可
- B. 模拟判断即可
- C. 枚举所有情况排序即可
- D. 分区间和与跨区间和,后者为一个前后缀+区间和,分别判断即可
- E. 贪心选强度最小的粘液,优先队列维护即可
- F. 考虑除以,枚举,统计满足且的的和,即做两次D题的做法即可。
A - aaaadaa (abc384 A)
题目大意
给定一个字符串和两个字符和,将所有非 替换为 。
解题思路
依次枚举每个字符判断即可。
神奇的代码
_, a, b = input().strip().split() c = input().strip() d = ''.join([b if x != a else x for x in c]) print(d)
B - ARC Division (abc384 B)
题目大意
打比赛,根据当前rating
和表现分a
更新rating
。
两类比赛:
- Div1:
rating
在1600-2799之间,rating
增加a
- Div2:
rating
在1200-2399之间,rating
增加a
如果rating
不在范围内,不会增加rating
。
给了n
场比赛的rating
和a
,求最终rating
。
解题思路
按照题意模拟即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, r; cin >> n >> r; while (n--) { int d, a; cin >> d >> a; if (d == 1 && 1600 <= r && r <= 2799) { r += a; } else if (d == 2 && 1200 <= r && r <= 2399) { r += a; } } cout << r << '\n'; return 0; }
C - Perfect Standings (abc384 C)
题目大意
给定五道题的分数,对于一个字符串,其分数为字符对应题目的分数之和。
共有 种情况,按照分数从高到低输出,如果分数相同,按照字典序输出。
解题思路
用DFS
或迭代的方法枚举所有情况,然后排序输出即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); array<int, 5> a; for (auto& x : a) cin >> x; vector<string> t1{""}; for (int i = 0; i < 5; ++i) { vector<string> t2 = t1; for (auto& x : t1) { auto y = x + char('A' + i); t2.push_back(y); } t2.swap(t1); } auto score = [&](const string& x) { int res = 0; for (auto c : x) { res += a[c - 'A']; } return res; }; sort(t1.begin(), t1.end(), [&](const string& x, const string& y) { int sx = score(x), sy = score(y); if (sx != sy) return sx > sy; return x < y; }); t1.pop_back(); for (auto& x : t1) cout << x << '\n'; return 0; }
D - Repeated Sequence (abc384 D)
题目大意
给定一个 周期序列,其中前 项为 。
判断是否存在一个和为 的非空连续子序列。
解题思路
所有数都是正数。
这个非空序列只有两种情况:
- 在一个周期内,很显然就是一个区间和的问题,转换成两个前缀和的差,用
set
存储前缀和判断是否存在即可。 - 跨越多个周期,此时就是一个后缀+一个前缀和若干个区间和的问题,枚举后缀,计算区间和数量,再判断对应前缀是否存在即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; LL s; cin >> n >> s; vector<LL> a(n); for (auto& x : a) cin >> x; vector<LL> sum(a); partial_sum(a.begin(), a.end(), sum.begin()); set<LL> st{0}; bool ok = false; for (auto x : sum) { if (st.count(x - s)) { ok = true; break; } st.insert(x); } if (!ok) { reverse(a.begin(), a.end()); LL tot = accumulate(a.begin(), a.end(), 0ll); LL sufsum = 0; for (auto& i : a) { sufsum += i; LL left = (s - sufsum) % tot; if (st.count(left)) { ok = true; break; } } } if (ok) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
E - Takahashi is Slime 2 (abc384 E)
题目大意
给定一个 的网格,每个单元格中有一个整数 ,以及一个整数 和初始位置 。
初始时,高桥位于 单元格中,强度为 。
现在高桥可以上下左右移动,每次移动后,高桥会吸收与其相邻的粘液中强度严格小于高桥强度的 倍的粘液,并将其吸收,高桥的强度会增加被吸收粘液的强度。
问高桥最终的强度是多少。
解题思路
很显然,我们肯定选择强度最小的粘液吸收,由于粘液会随着高桥的移动越来越多,可以用一个优先队列来维护当前可接触到的粘液的最小值,然后贪心选粘液强度最小值即可。
注意会超出long long
范围,需要用__int128
来存储。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int h, w, x, p, q; cin >> h >> w >> x >> p >> q; vector<vector<LL>> a(h, vector<LL>(w)); for (auto& i : a) for (auto& j : i) cin >> j; priority_queue<array<LL, 3>, vector<array<LL, 3>>, greater<array<LL, 3>>> pq; array<int, 4> dx = {1, 0, -1, 0}; array<int, 4> dy = {0, 1, 0, -1}; --p, --q; for (int i = 0; i < 4; i++) { int nx = p + dx[i]; int ny = q + dy[i]; if (nx < 0 || nx >= h || ny < 0 || ny >= w) continue; pq.push({a[nx][ny], nx, ny}); a[nx][ny] = -1; } LL cur = a[p][q]; a[p][q] = -1; while (!pq.empty()) { auto [s, u, v] = pq.top(); if (__int128(s) * x >= cur) { break; } pq.pop(); cur += s; for (int i = 0; i < 4; i++) { int nx = u + dx[i]; int ny = v + dy[i]; if (nx < 0 || nx >= h || ny < 0 || ny >= w || a[nx][ny] == -1) continue; pq.push({a[nx][ny], nx, ny}); a[nx][ny] = -1; } } cout << cur << '\n'; return 0; }
F - Double Sum 2 (abc384 F)
题目大意
定义 表示将 的幂因子全部去掉后的数。
给定长度为 的整数序列 ,求 。
解题思路
建议看第二个做法,该做法是赛时的做法,分情况讨论复杂了,写题解时发现第二类情况的做法具有通用型。
首先我们可以将 分解为 ,其中 为奇数,然后对进行排序。
然后考虑两个数,根据其 是否相同,可以分为两种情况:
- ,即 ,此时 ,因为
奇+偶=奇
。所以我们可以枚举 ,考虑所有的 的贡献,设其数量为 ,对应的 的和为 ,那么 。 - ,此时 ,由于
奇+奇=偶
,所以仍要除以的幂,但关键是幂是多少呢?诚然,我们可以直接加然后求幂,但这样复杂度必然会变成。不能这样做。
注意到,因此幂的可能情况不超过种,我们可以枚举幂,然后统计每个幂,有多少的是的倍数。注意这个的统计和做法一样:如果 是 的倍数,就意味着 ,即 ,我们可以枚举,用set
统计符合条件的的数量即可,通过维护对应取模值为的的和及数量,就能得到满足的,该幂其对答案的贡献就是。
但这并不是正确的,考虑才是奇数,再上述计数下,最终的和包括了 ,实际上只有对答案才有贡献(它才是的值),其余都不是,我们得剔除其余部分。
怎么剔除呢?注意到是等比数列,设,则,则
所以,左右两边对 求和,就是。第一项就是所有的俩俩相加,第二项就是我们通过上述方法求得和。这样就得到了答案。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for (auto& i : a) cin >> i; vector<int> c2(n); auto calc = [](int x) { int ret = 0; while (~x & 1) { x >>= 1; ++ret; } return ret; }; int up = 25; vector<int> p2(up); p2[0] = 1; for (auto i = 1; i < up; ++i) { p2[i] = p2[i - 1] << 1; } LL ans = 0; for (auto i = 0; i < n; ++i) { c2[i] = calc(a[i]); ans += a[i] / p2[c2[i]]; } vector<int> id(n); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int x, int y) { return c2[x] < c2[y]; }); LL tot = accumulate(a.begin(), a.end(), 0ll); int cnt = n; auto solve = [&](auto l, auto r) { LL ret = 0; for (int i = up - 1; i >= 1; --i) { map<int, LL> sum, cnt; for (auto j = l; j != r; j = next(j)) { int mod = a[*j] % p2[i]; int other = mod ? (p2[i] - mod) : 0; ret += (sum[other] + 1ll * cnt[other] * a[*j]) / p2[i]; sum[mod] += a[*j]; cnt[mod] += 1; } } LL all = 0; LL pre = 0; for (auto j = l; j != r; j = next(j)) { all += pre + 1ll * a[*j] * (j - l); pre += a[*j]; } ret = all - ret; return ret; }; for (auto i = id.begin(); i != id.end();) { LL ss = a[*i]; LL cnt2 = 1; tot -= a[*i]; cnt -= 1; auto nxt = next(i); while (nxt != id.end() && c2[*nxt] == c2[*i]) { tot -= a[*nxt]; cnt -= 1; ss += a[*nxt]; cnt2 += 1; nxt = next(nxt); } ans += 1ll * cnt2 * tot / p2[c2[*i]] + 1ll * cnt * (ss) / p2[c2[*i]]; ans += solve(i, nxt); i = nxt; } cout << ans << '\n'; return 0; }
写到这里忽然发现这种做法也能覆盖的情况。即,直接枚举必然是 ,所以我们转而枚举,即,然后设法统计这样的的和,对所有的相加,即为答案。
怎么统计呢?统计的和的方法形同D题做法,即枚举,然后用map
维护其对应取模的值和数量,但上述要求不包含的情况,那就直接从减去即可,这样就能得到但不是的情况。
得用unordered_map
来维护,map
可能会超时。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for (auto& i : a) cin >> i; constexpr int up = 26; vector<int> p2(up); p2[0] = 1; for (auto i = 1; i < up; ++i) { p2[i] = p2[i - 1] << 1; } LL ans = 0; array<unordered_map<int, LL>, up> sum, cnt; for (auto j = 0; j < n; j++) { for (int i = up - 2; i >= 0; --i) { int mod = a[j] % p2[i]; int other = mod ? (p2[i] - mod) : 0; int mod2 = a[j] % p2[i + 1]; int other2 = mod2 ? (p2[i + 1] - mod2) : 0; sum[i][mod] += a[j]; cnt[i][mod] += 1; ans += (sum[i][other] - sum[i + 1][other2] + (cnt[i][other] - cnt[i + 1][other2]) * a[j]) / p2[i]; } } cout << ans << '\n'; return 0; }
G - Abs Sum (abc384 G)
题目大意
给定长度为 的整数序列 和 以及长度为 的整数序列 和 。
求每个 的长度 。
解题思路
<++>
神奇的代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2019-12-14 Codeforces Round #606 (Div. 2, based on Technocup 2020 Elimination Round 4)