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 国际」许可协议进行许可。

posted @   sxl701817  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu