AtCoder Beginner Contest 351
A - The bottom of the ninth (abc351 A)
题目大意
给定个 和 个 ,问最后一个 是多少,使得 。
解题思路
答案就是。
神奇的代码
a = sum(map(int, input().split())) b = sum(map(int, input().split())) print(a - b + 1)
B - Spot the Difference (abc351 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<string> a(n), b(n); for (auto& x : a) cin >> x; for (auto& x : b) cin >> x; auto solve = [&]() { for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (a[i][j] == b[i][j]) continue; return make_pair(i, j); } } return make_pair(-1, -1); }; auto [x, y] = solve(); cout << x + 1 << ' ' << y + 1 << '\n'; return 0; }
C - Merge the balls (abc351 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); int n; cin >> n; vector<int> team; while (n--) { int x; cin >> x; team.push_back(x); while (team.size() > 1) { int a = team.back(); int b = team[team.size() - 2]; if (a == b) { team.pop_back(); team.pop_back(); team.push_back(a + 1); } else { break; } } } cout << team.size() << '\n'; return 0; }
D - Grid and Magnet (abc351 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; cin >> h >> w; vector<string> a(h); for (auto& x : a) cin >> x; bool empty = false; vector<vector<int>> stop(h, vector<int>(w, 0)); array<int, 4> dx = {1, 0, -1, 0}; array<int, 4> dy = {0, 1, 0, -1}; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (a[i][j] == '#') { stop[i][j] = 1; for (int k = 0; k < 4; k++) { int ni = i + dx[k]; int nj = j + dy[k]; if (ni >= 0 && ni < h && nj >= 0 && nj < w) { stop[ni][nj] = 1; } } } if (a[i][j] == '.') { empty = true; } } } vector<vector<int>> visited(h, vector<int>(w, 0)); int ans = empty; int tt = 0; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (visited[i][j] != 0 || stop[i][j]) { continue; } ++tt; queue<pair<int, int>> q; q.push({i, j}); visited[i][j] = tt; int cnt = 0; while (!q.empty()) { auto [x, y] = q.front(); q.pop(); cnt++; if (stop[x][y]) { continue; } for (int k = 0; k < 4; k++) { int nx = x + dx[k]; int ny = y + dy[k]; if (nx >= 0 && nx < h && ny >= 0 && ny < w && visited[nx][ny] < tt) { visited[nx][ny] = tt; q.push({nx, ny}); } } } ans = max(ans, cnt); } } cout << ans << '\n'; return 0; }
E - Jump Distance Sum (abc351 E)
题目大意
二维网格,个点,一次可以往四个角的方向走。定义 为从需要的最小步数。若不能到达,则假定步数为。
求。
解题思路
考虑怎样的情况是不能到达的。容易发现,一个格子不能到达其上下左右相邻的格子。
更进一步的,对每个点,当移动时,观察 的变化 ,会发现只有三种情况:,无论哪种,其 的奇偶性不变。因此可以得到 奇偶性不同的点无法相互到达。相同的点或许可以到达(实际上是可以的)。
先对所有点根据 的奇偶性分类,考虑同类别的,其 如何计算。
简单起见,考虑 ,且。每次移动,对于每个座标上的值,都得 ,假设 ,为了最小步数,那我肯定每次选择的动作,都会让 。至于 ,如果每次也 ,那最终就超过了 ,因此中间需要一些 ,来避免超过 。即一开始先 ,当 后,就 和 交替,以保持 不变,同时 。
但这样最后能到达 吗?就看是否满足 和 数量相等。当时,还需要执行 个步骤,如果 是偶数,则和 数量相等,可行。事实上,因为和 奇偶性相同,并且 ,因此 的奇偶性也相同,则 必定是偶数,因此也一定能到达 。
由上述分析可以得知, 。
朴素求和就是,棘手在上,注意到上述距离实际上是切比雪夫距离,可以
绝对值去掉的方式比较套路,可以对 排序,然后按顺序遍历 ,这样 就始终是一个符号,但是外层还套了一个 比较棘手。
注意到上述距离实际上是切比雪夫距离,可以将其转换成曼哈顿距离。得到新点,然后计算两点的曼哈顿距离,即两维度的差的绝对值的和。其中 维度和 维度是可以分开算的,因此就按照上述绝对值去掉的方式,维护前缀和,计算求和即可。
时间复杂度是。
神奇的代码
#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; array<vector<pair<int, int>>, 2> p{}; for (int i = 0; i < n; i++) { int x, y; cin >> x >> y; p[(x + y) & 1].push_back({x + y, x - y}); } auto solve_one = [&](vector<int>& x) -> LL { sort(x.begin(), x.end()); LL ans = 0, sum = 0; for (int i = 0; i < x.size(); i++) { ans += 1LL * x[i] * i - sum; sum += x[i]; } return ans; }; auto solve = [&](vector<pair<int, int>>& p) -> LL { vector<int> x, y; for (auto& [a, b] : p) { x.push_back(a); y.push_back(b); } return solve_one(x) + solve_one(y); }; LL ans = solve(p[0]) + solve(p[1]); ans /= 2; cout << ans << '\n'; return 0; }
F - Double Sum (abc351 F)
题目大意
给定个数 ,计算
解题思路
将求和式变一下,即为
即两个二维偏序问题,一个关于的计数问题,一个关于的 求和问题。
二维偏序的解法,假想在一个二维坐标系内,就是问一个矩形的和。
可通过循环满足一个不等式关系(即满足一维),再用一个数据结构维护另一个不等式关系(维护另一维)求和。
以第一个偏序问题为例,即枚举下标,把小于 的加入到数据结构中(满足了 ),然后在满足小于范围求和,此为一个区间查询操作。故可以建一棵树状数组或线段树维护此查询操作。其下标的含义是而不是 。由于这里的 高达 ,但数量不超过 ,因此需要事先离散化。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; // starting from 1 template <typename T> class fenwick { public: vector<T> fenw; int n; fenwick(int _n) : n(_n) { fenw.resize(n); } inline int lowbit(int x) { return x & -x; } void modify(int x, T v) { for (int i = x; i < n; i += lowbit(i)) { fenw[i] += v; } } T get(int x) { T v{}; for (int i = x; i > 0; i -= lowbit(i)) { v += fenw[i]; } return v; } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); set<int> candidate; for (auto& x : a) { cin >> x; candidate.insert(x); } map<int, int> rank; for (auto x : candidate) { rank[x] = rank.size() + 1; } fenwick<LL> presum(rank.size() + 1), cnt(rank.size() + 1); LL ans = 0; for (int i = 0; i < n; ++i) { int pos = rank[a[i]]; ans += 1ll * a[i] * cnt.get(pos) - presum.get(pos); cnt.modify(pos, 1); presum.modify(pos, a[i]); } cout << ans << '\n'; return 0; }
G - Hash on Tree (abc351 G)
题目大意
给定一棵有根树,根是。点有权值,定义树的权值为。
的计算方式为:
- 若 是叶子,则 。
- 否则,
执行次操作,每次操作修改一个点的权值,回答修改后的树的权值,即。
解题思路
<++>
神奇的代码
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18172416
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步