Atcoder ABC 370题解
前言
本题解部分思路来源于网络,仅供参考。
A - Raise Both Hands
题目大意#
如果 $ Snuck $ 想吃章鱼烧则只举起左手,如果不想吃章鱼烧则只举起右手。如果同时举起或放下两只手则输出 Invalid
。
解题思路#
根据题意模拟即可。
code#
#include <bits/stdc++.h> using namespace std; int main() { int l, r; scanf("%d%d", &l, &r); if (l == r) puts("Invalid"); else if (l == 1) puts("Yes"); else puts("No"); return 0; }
B - Binary Alchemy
题目大意#
给定一个 \(N\) 种元素,将元素 \(1,\ 2,\ ...\ ,N\) 合并后得到的结果是什么?
- 将 \(i,j\) 元素合并时,如果 \(i \geqslant j\) ,则合并结果为 \(A_{i,j}\) ;否则合并结果为 \(A_{j,i}\) 。
解题思路#
根据题意模拟即可。
code#
#include <bits/stdc++.h> using namespace std; int a[105][105]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { scanf("%d", &a[i][j]); } } int ans = a[1][1]; for (int i = 2; i <= n; i++) { if (ans < i) ans = a[i][ans]; else ans = a[ans][i]; } printf("%d\n", ans); return 0; }
C - Word Ladder
题目描述#
给定两个字符串 \(S\) 和 \(T\) ,求出如何从 \(S\) 变换到 \(T\) ,输出字典序最小的结果。
解题思路#
因为要输出字典序最小的结果,所以可以每次选取最小的可能的结果,最后的结果也会是最小的。应为 \(S\) 和 \(T\) 的长度最多为 \(100\) ,所以这个程序不需要任何优化。时间复杂度
#include <bits/stdc++.h> using namespace std; vector<string> vec; int main() { string s, t; cin >> s >> t; int n = s.size(); string tmp, tt; while (s != t) { tmp = ""; for (int i = 0; i < n; i++) { tmp += 'z'; } for (int i = 0; i < n; i++) { if (s[i] != t[i]) { tt = s; tt[i] = t[i]; tmp = min(tmp, tt); } } s = tmp; vec.push_back(tmp); } cout << vec.size() << endl; for (string i : vec) { cout << i << endl; } return 0; }
D - Cross Explosion
题目描述#
给定一个 \(H \times W\) 的网格,最开始每一个格子上都有墙,将在某些位置按顺序投放炸弹。如果投放炸弹的地方有墙,则炸掉这堵墙。否则,炸掉上下左右看到的第一堵墙。
解题思路#
因为上下左右爆炸有四个方向,所以可以看成在四个 1D 空间爆炸。对于每一个 1D 空间,我们需要一个数据结构
-
快速地找到下一堵墙。
-
快速的删除一堵墙。
因为这两个特性,应到我们想到 set。所以我们可以对于每一行每一列维护两个 set ,一个正向排序一个逆向排序。之后,这个问题就变成模拟了。
code#
#include <bits/stdc++.h> using namespace std; set<int> T[400005], L[400005]; set<int, greater<>> B[400005], R[400005]; void del(int r, int c) { T[r].erase(c); B[r].erase(c); L[c].erase(r); R[c].erase(r); } int main() { int h, w, q; scanf("%d%d%d", &h, &w, &q); for (int i = 1; i <= h; i++) { for (int j = 1; j <= w; j++) { T[i].insert(j); B[i].insert(j); L[j].insert(i); R[j].insert(i); } } int r, c; int ans = h * w; set<int>::iterator ia, ic; set<int, greater<>>::iterator ib, id; while (q--) { scanf("%d%d", &r, &c); if (T[r].find(c) != T[r].end()) { del(r, c); ans--; } else { ia = T[r].upper_bound(c); ib = B[r].upper_bound(c); ic = L[c].upper_bound(r); id = R[c].upper_bound(r); if (ia != T[r].end()) { ans--; del(r, *ia); } if (ib != B[r].end()) { ans--; del(r, *ib); } if (ic != L[c].end()) { ans--; del(*ic, c); } if (id != R[c].end()) { ans--; del(*id, c); } } } printf("%d\n", ans); return 0; }
E - Avoid K Partition
题目大意#
给定一个序列 \(A=(A_1,A_2,\ ...\ ,A_n)\) ,求有多少种方案分割序列使得分割出来的每一个序列的总和不等于 \(K\) 。
解题思路#
容易想到一种朴素解法:
- 定义 \(dp_i\) 为前 \(i\) 个数的最大分割方案数。则动态转移方程为 \(dp_i = \sum_{1 \leqslant j \leqslant i,sum(j + 1,i)\neq K}dp_j\) 。
动态转移方程中的 \(sum(j + 1,i)\) 可以用前缀和优化。令 \(sum[i]\) 为前 \(i\) 个数的和,则动态转移方程变为 \(dp_i = \sum_{1 \leqslant j \leqslant i,sum[i] - sum[j]\neq K}dp_j\) 。或者说,要求出满足 \(sum[j] \neq sum[i] - K\) 的 \(dp_j\) 的和。为了求出满足这个条件的数,我们令 \(cnt[i] = \sum_{sum[j]=i} dp[j]\) ,则动态转移方程变为 \(dp_i = \sum_{1 \leqslant j \leqslant i}dp_j - cnt[sum[i] - K]\) 。这样,时间复杂度就降下来了。
code#
#include <bits/stdc++.h> #define MOD 998244353 using namespace std; using ll = long long; int a[200005]; int main() { ll n, k; scanf("%lld%lld", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", a + i); } map<ll, int> m; m[0] = 1; ll summ = 0, sum = 1, tmp; for (int i = 1; i <= n; i++) { summ += a[i]; tmp = (sum - m[summ - k] + MOD) % MOD; m[summ] = (m[summ] + tmp) % MOD; sum = (sum + tmp) % MOD; } printf("%lld\n", tmp); return 0; }
F - Cake Division
题目大意#
有一块蛋糕,上面有\(n\) 条分割线。从中选取 \(k\) 个分割线把蛋糕切开,将分割出来的 \(k\) 块蛋糕分给 \(k\) 个人。求分到蛋糕最少的那个人最多可以分到多少?该怎么分?
解题思路#
“求分到蛋糕最少的那个人最多可以分到多少” 这句话引导我们想到二分查找。所以我们用二分查找的话,问题就变成了“求分到蛋糕最少的人蛋糕重量有没有 \(x\) ”。那么,应该如何求出这个问题呢?首先,套路性的化环为链,然后对于每一种可能,求出当每一块蛋糕的重量都达到 \(x\) 时,蛋糕数量有没有达到 \(k\) 。为了快速求出这个问题,我们可以使用倍增。令 \(up[i][j]\) 为以第 \(i\) 位开头,分了 \(2^j\) 块重量大于等于 \(x\) 的蛋糕需要分割到第几条分割线。如果 \(up[i][j]\) 小于 \(i + n\) ,那么从 \(i\) 开始,每块蛋糕重量大于 \(x\) 时可行的。否则就是不可行的。我们只需要求出有多少种方案,我们就可以知道分到蛋糕最少的人蛋糕重量有没有 \(x\) 和如果分到蛋糕最少的人蛋糕重量有 \(x\) 则需要切几刀。
code#
#include <bits/stdc++.h> using namespace std; int a[400010]; int n, k; int N; int check(int x) { vector<array<int, 25>> up(N + 2); up[N + 1][0] = N + 1; queue<int> window; int r = 1, sum = 0; for (int i = 1; i <= N; i++) { while (r <= N && sum < x) { window.push(a[r]); sum += a[r]; r++; } if (sum < x) up[i][0] = N + 1; else up[i][0] = r; sum -= window.front(); window.pop(); } for (int i = 1; i <= 20; i++) { for (int j = 1; j < N + 2; j++) { up[j][i] = up[up[j][i - 1]][i - 1]; } } int cnt = 0; for (int i = 1; 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; } int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", a + i); a[i + n] = a[i]; } N = 2 * n; int l = 1, r = 2e9 + 8; while (l + 1 < r) { int mid = l + (r - l) / 2; if (check(mid)) l = mid; else r = mid; } int cnt = check(l); printf("%d %d\n", l, n - cnt); return 0; }
作者:Esofar
出处:https://www.cnblogs.com/sxloiblog/p/18403570
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?