AtCoder Beginner Contest 370
A - Raise Both Hands (abc370 A)
题目大意
给出Snuke举的左右手情况,如果只举左手,输出Yes
,如果只举右手,输出No
,否则输出Invalid
。
解题思路
逐一判断即可。
神奇的代码
#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 l, r; cin >> l >> r; if (l == 1 && r == 0) cout << "Yes" << '\n'; else if (l == 0 && r == 1) cout << "No" << '\n'; else cout << "Invalid" << '\n'; return 0; }
B - Binary Alchemy (abc370 B)
题目大意
给定物品合成成分表表示物品 和物品 合成物品 。
问物品 ,依次与 物品合成,问最后的物品。
解题思路
按照题意查表,模拟合成即可。
神奇的代码
#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<vector<int>> a(n); for (int i = 0; i < n; i++) { a[i].resize(i + 1); for (auto& x : a[i]) { cin >> x; --x; } } int cur = 0; for (int i = 0; i < n; ++i) { int x = cur, y = i; if (x < y) swap(x, y); cur = a[x][y]; } cout << cur + 1 << '\n'; return 0; }
C - Word Ladder (abc370 C)
题目大意
给定两个字符串。
用最小的次数,使得 ,并且字符串的字典序最小。
操作为,选择 ,并且将修改后的 放入 的末尾。
解题思路
如何次数最小呢?
依次考虑从左到右的每一位 ,如果 ,那我肯定要 ,但这是我们此时要进行的操作吗?还是先放一放,改后面的字母后,再改当前位?
由于每次会将修改后的放入 的末尾,因此我们要优先考虑首先进行的操作,应该是:即刻进行,还是缓一缓在进行。
如果,那就优先更改当前位,这样改后的 的字典序更小。
如果 ,那就先更改后面位的,最后再改当前位,这样得到的 的字典序最小。
这种回溯的感觉,可以用实现上述操作。
神奇的代码
#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); string s, t; cin >> s >> t; vector<string> ans; auto dfs = [&](auto dfs, int pos) { if (pos == s.size()) { return; } if (s[pos] == t[pos]) { dfs(dfs, pos + 1); } else if (s[pos] < t[pos]) { dfs(dfs, pos + 1); s[pos] = t[pos]; ans.push_back(s); } else { s[pos] = t[pos]; ans.push_back(s); dfs(dfs, pos + 1); } }; dfs(dfs, 0); cout << ans.size() << '\n'; for (auto& i : ans) cout << i << '\n'; return 0; }
D - Cross Explosion (abc370 D)
题目大意
二维网格,初始每个格子有墙。
依次进行次放炸弹的操作,给定每次放炸弹的位置 ,如果该位置有墙,则该墙消失。
否则,炸弹会爆炸,会产生十字冲击波,该位置上下左右的各第一个墙都会消失。
问最后还存在的墙的数量。
解题思路
对于第一种情况,直接移除该位置的墙即可。
对于第二种情况,需要找到该列上下、该行左右最近的墙。
墙的数量,可以储存每个墙的坐标。
然后对于每行和每列,分别维护表示第行还有墙的列坐标,是个,表示第 列还有墙的行坐标 ,也是个。
这样,对于一个炸弹 ,如果该位置没有墙,则需要找到 的最大和 的最小的数字。同理对于也要找对应的数字,然后 。这样每次操作的复杂度都是 ,总的时间复杂度就是 。
代码对于没有墙的逻辑是:
- , 由于没有墙,此时一定 (否则是 ),如果 ,那么它就是下面的第一个墙(这里认为左上是原点),要毁掉,于是, 返回值是移除了该 后的下一个元素。
- 然后要找上面的第一个墙,此时 是 的第一个位置(无论刚刚是否了),因此如果 ,那么 就是上面的第一个墙,要毁掉,于是 。
同理的思路处理 即可。
神奇的代码
#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, q; cin >> h >> w >> q; vector<int> hh(h), ww(w); iota(hh.begin(), hh.end(), 0); iota(ww.begin(), ww.end(), 0); vector<set<int>> hset(h), wset(w); for (int i = 0; i < h; i++) { hset[i].insert(ww.begin(), ww.end()); } for (int i = 0; i < w; i++) { wset[i].insert(hh.begin(), hh.end()); } while (q--) { int x, y; cin >> x >> y; --x, --y; auto it = hset[x].lower_bound(y); if (it != hset[x].end() && *it == y) { hset[x].erase(y); wset[y].erase(x); } else { if (it != hset[x].end()) { wset[*it].erase(x); it = hset[x].erase(it); } if (it != hset[x].begin()) { it = prev(it); wset[*it].erase(x); hset[x].erase(it); } it = wset[y].lower_bound(x); if (it != wset[y].end()) { hset[*it].erase(y); it = wset[y].erase(it); } if (it != wset[y].begin()) { it = prev(it); hset[*it].erase(y); wset[y].erase(it); } } } int cnt = 0; for (int i = 0; i < h; i++) { cnt += hset[i].size(); } cout << cnt << '\n'; return 0; }
E - Avoid K Partition (abc370 E)
题目大意
给定一个数组,划分成若干个子区间,使得没有子区间的和为 。
求划分方案数。
解题思路
朴素就是设 表示前 段划分满足条件的方案数。
转移则枚举最后一次的区间,然后 。
复杂度显然是 的。
棘手在条件 上,如果没有这个条件,这个转移其实就是一个前缀和,用前缀和优化即为 。
我们用前缀和相减代替区间和,即 ,转移式即为。
换句话说,我们要对的 求和,这是个非常稀疏的条件,即如果设 ,即前缀和为 的 值,那上述转移式可改写成。
即一个前缀和与一个数的差值,这样转移就是了,因此维护一个前缀和 以及前缀和的和即可。
时间复杂度就是。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; const int mo = 998244353; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; LL k; cin >> n >> k; vector<int> a(n); for (auto& x : a) cin >> x; map<LL, int> cnt; cnt[0] = 1; LL presum = 0; int precnt = 1; int ans = 0; for (auto& i : a) { presum += i; ans = (precnt - cnt[presum - k] + mo) % mo; cnt[presum] = (cnt[presum] + ans) % mo; precnt = (precnt + ans) % mo; }; cout << ans << '\n'; return 0; }
F - Cake Division (abc370 F)
题目大意
给定一个环形数组,划分为段,使得每段和的最小值最大。
在该最大值的各种划分方案中,求有多少位置,在所有划分方案中都不被分开。
解题思路
如果我们确定了这个每段和的最小值
,且是一个链的情况,我们有个贪心的策略:从第一个数开始往右延伸,直到第一个不小于的位置,有,它们就是一段,我们定义,如果不存在可行的话,。表示分了一段区间。如果能往复能分成段则可行。时间复杂度是 或 。后者就是二分来找到每一段的最右端。
容易发现这个 与是否可行具有单调性: 越大,越难可行, 越小,越容易可行。因此可以通过二分找到这个 。
然后考虑环的情况,一种处理方法是考虑每个起点,每个起点都做一次上述验证,如果存在一个起点满足上述要求则可行。这样验证的复杂度是 。
如何优化呢?
由于环形,我们拆成链,然后复制一份。注意到以每个位置为起点,计算的值,可以通过的滑动窗口得到。这就是分一段的区间,而如果分两段就是。注意到只要起点固定,它会延伸到哪里也固定了,不同段之间也相互独立。因此函数可以简单的复合起来,相比于一次一次分,优化方向就是以倍增的形式分段。
即二分了后,预处理倍增数组 表示从 开始,分了 段后的右边界 ,即 包含了满足题意的 段。只要 ,那么从 开始就可行的。通过倍增数组来分段,每个起点的验证复杂度就降为 。
最后求有多少位置是不会被断开的,就在枚举起点的时候,如果不可行,那么该起点与上一个数之间就不能断开(断开了就是以该数为起点,经求得是不可行的),因此后者就是不可行的起点数量。
总的时间复杂度就是
神奇的代码
#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, k; cin >> n >> k; vector<int> a(n + n); for (int i = 0; i < n; ++i) { cin >> a[i]; a[i + n] = a[i]; } int N = n + n; int l = 1, r = 2e9 + 8; auto check = [&](int x) { vector<array<int, 20>> up(N + 2); up[N][0] = N + 1; up[N + 1][0] = N + 1; queue<int> windows; int r = 0; int sum = 0; for (int i = 0; i < N; ++i) { while (r < N && sum < x) { windows.push(a[r]); sum += a[r]; ++r; } if (sum < x) up[i][0] = N + 1; else up[i][0] = r; sum -= windows.front(); windows.pop(); }; for (int i = 1; i < 20; ++i) for (int j = 0; j < N + 2; ++j) { up[j][i] = up[up[j][i - 1]][i - 1]; } int cnt = 0; for (int i = 0; i < n; ++i) { int pos = i; for (int j = 0; j < 20; ++j) { if ((k >> j) & 1) { pos = up[pos][j]; } } if (pos <= i + n) { ++cnt; } } return cnt; }; while (l + 1 < r) { int mid = l + (r - l) / 2; if (check(mid)) l = mid; else r = mid; } int cnt = check(l); cout << l << ' ' << n - cnt << '\n'; return 0; }
G - Divisible by 3 (abc370 G)
题目大意
如果一个数是好的,说明它的因子和能被整除。
给定 ,问一个长度为 的数组 的数量,满足其各数的乘积不超过 ,且是好数。
解题思路
<++>
神奇的代码
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18402239
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步