Atcoder Beginner Contest 342 全题解
A - Yay!
题意
-
给定字符串
。 -
已知该字符串中只有一个字符与其他字符不同。
-
求这个字符。
思想
开一个数组
选择
代码
#include <bits/stdc++.h>
using namespace std;
int cnt[26], f[26];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s;
cin >> s;
int n = s.size();
for (int i = 0; i < n; i++) {
cnt[s[i] - 'a']++;
if (cnt[s[i] - 'a'] == 1)
f[s[i] - 'a'] = i;
}
for (int i = 0; i < 26; i++) {
if (cnt[i] == 1) {
cout << f[i] + 1 << endl;
}
}
return 0;
}
B - Which is ahead?
题意
-
有
个人站成一排,从前往后第 个人是 。 -
处理
次询问。 -
每次询问
与 哪个更靠前,输出人的编号。
思想
每次询问从前向后扫描,先扫描到
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 105;
int n, q;
int p[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> p[i];
cin >> q;
for (int i = 1; i <= q; i++) {
int a, b;
cin >> a >> b;
int x, y;
bool flag = false;
for (int j = 1; j <= n; j++) {
if (p[j] == a) {
if (!flag) {
cout << a << endl;
}
flag = true;
}
if (p[j] == b) {
if (!flag) {
cout << b << endl;
}
flag = true;
}
}
}
return 0;
}
C - Many Replacement
题意
-
给你一个长度为
的字符串 。 -
有
次修改。 -
每次修改将
中的所有字符 更改为 。 -
输出最后的字符串。
思想
如果直接按照题目要求模拟的话,时间复杂度为
考虑记录每个字符
动态维护
每次更改将所有会更改为
即对于所有
最后将
时间复杂度
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 2e5 + 5;
int n, q;
string s;
int to[26], ans[26];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
cin >> s;
cin >> q;
for (int i = 0; i < 26; i++)
to[i] = i;
while (q--) {
string c1, d1;
cin >> c1 >> d1;
int c = c1[0] - 'a';
int d = d1[0] - 'a';
for (int i = 0; i < 26; i++) {
if (to[i] == c) {
to[i] = d;
}
}
}
for (int i = 0; i < n; i++) {
s[i] = to[s[i] - 'a'] + 'a';
}
cout << s << endl;
return 0;
}
D - Square Pair
题意
-
给你一长度为
的序列 。 -
求
为平方数的无序数对 的个数。
思想
如果直接按照题意模拟复杂度高达
根据算数基本定理,我们将
考虑什么时候
若
此时要让他为平方数则要求:
我们可以给
这样只需要
但是这里有个特例:
所以我们先把
则
所以答案的贡献先算上
我们接着把每一个
若这一种的数量为
按照此方法计算即可。
此处
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
constexpr int MAXN = 2e5 + 5;
int n;
int a[MAXN];
map<vector<int>, int> mp;
void build(int x) {
vector<int> ans;
map<int, int> tmp;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
while (x % i == 0) {
x /= i;
tmp[i]++;
}
}
}
if (x > 1) {
tmp[x]++;
}
for (auto [i, j] : tmp) {
if ((j & 1) == 0)
continue;
ans.push_back(i);
}
mp[ans]++;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
long long ans = 0;
long long cnt = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] == 0)
cnt++;
else build(a[i]);
}
ans += cnt * (n - cnt) + cnt * (cnt - 1) / 2;
for (auto [i, j] : mp) {
ans += 1ll * j * (j - 1) / 2;
}
cout << ans << endl;
return 0;
}
E - Last Train
题意
-
有
个站台, 种列车。 -
第
种列车有 次单程车。 -
第一次单程车发车于
。 -
每次单程车都需要花
单位的时间来到达终点。 -
每相邻两次单程车发车时间相距
单位时间。 -
出发站为
。 -
结束站为
。 -
求对于每个点
求出 即从 号站出发要能到达 号站的最晚时间。
思想
由题面可知这是一道图论题。
感性理解一下这是一个类似最短路的问题。
题中的列车网络是一个有向图,但不一定无环。
这样拓扑排序就被 pass 了。
如果是 BFS 它又有边权。
观察一下,不可能其他都一样只是多坐了一班车还更优。
所以我们选用堆优化
注意要反向建图,本题是单终最短路。
我们可以按照最优顺序来排序
如何松弛呢?
考虑计算要在
其中
我们选择最大的
我们注意到
如果错过了这班车就不能松弛,但是如果太早了可以等。
错过这班车即
太早了等即
我们把
我们只要发现
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
constexpr int MAXN = 2e5 + 5;
int n, m;
struct Node {
int l, d, k, c, to;
};
vector<Node> G[MAXN];
int f[MAXN];
void Dijkstra() {
memset(f, 0xc0, sizeof f);
f[n] = 0x3f3f3f3f3f3f3f3f;
set<pair<int, int>, greater<>> st;
st.insert(make_pair(0x3f3f3f3f3f3f3f3f, n));
while (st.size()) {
int u = st.begin()->second;
st.erase(st.begin());
for (auto [l, d, k, c, v] : G[u]) {
if (l + c > f[u])
continue;
if (f[v] < l + min((f[u] - c - l) / d, k - 1) * d) {
st.erase(make_pair(f[v], v));
f[v] = l + min((f[u] - c - l) / d, k - 1) * d;
st.insert(make_pair(f[v], v));
}
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int l, d, k, c, a, b;
cin >> l >> d >> k >> c >> a >> b;
G[b].push_back({ l, d, k, c, a });
}
Dijkstra();
for (int i = 1; i < n; i++) {
if (f[i] == 0xc0c0c0c0c0c0c0c0)
cout << "Unreachable" << endl;
else
cout << f[i] << endl;
}
return 0;
}
F - Black Jack
题意
-
你在跟一个叫庄稼的人玩游戏。
-
有一个有
面的色子,它会等概率地摇出 到 的整数。 -
你先摇,每一次都把摇出来的结果加到
上去,你可以选择摇几次。 -
庄稼后摇,只要它摇出来的点数
,就会继续摇,同样地,它会把每次摇出来的数加到 上。 -
如果
或 你就输了,否则你就赢了。 -
求你获胜的概率最大是多少。
思想
因为庄稼的策略是固定的,所以我们计算
注意到每次
我们就从小往大向后贡献。
但是这样会 TLE。
区间加和单点查询。
我们用差分树状数组来维护它。
我们为了计算小于等于的贡献就直接前缀和一下得到
我们计算出
我们先计算在
若
若
我们令这个函数为
然后我们令
如果继续摇答案就是
停下答案就是
所以:
我们从后往前扫描,用
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 4e5 + 5;
int lowbit(int x) {
return x & -x;
}
struct Fenwick {
double c[MAXN];
void upd(int l, int r, double x) {
for (int i = l + 1; i < MAXN; i += lowbit(i))
c[i] += x;
for (int i = r + 2; i < MAXN; i += lowbit(i))
c[i] -= x;
}
double ask(int x) {
double ans = 0;
for (int i = x + 1; i > 0; i -= lowbit(i))
ans += c[i];
return ans;
}
} fenwick;
int n, l, d;
double f[MAXN], g[MAXN];
double calc(int x) {
if (x > n)
return 0;
double ans = 1 - g[n];
if (x > l)
ans += g[x - 1];
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> l >> d;
fenwick.upd(0, 0, 1);
for (int i = 0; i <= 4e5; i++) {
g[i] = fenwick.ask(i);
if (i < l) {
fenwick.upd(i + 1, i + d, g[i] / d);
g[i] = 0;
}
}
for (int i = 1; i <= 4e5; i++)
g[i] += g[i - 1];
double sum = 0;
for (int i = 4e5; i >= 0; i--) {
if (i > n)
f[i] = 0;
else
f[i] = max(sum / d, calc(i));
sum += f[i];
if (i + d <= 4e5)
sum -= f[i + d];
}
cout << fixed << setprecision(15) << f[0] << endl;
return 0;
}
G - Retroactive Range Chmax
题意
-
给定一个长度为
的序列 。 -
有
次操作。 -
1 操作:对所有
进行 。 -
2 操作:取消第
次操作,保证第 为 1 操作,即新的状态为从前往后做除了 操作外的所有操作得到的状态。 -
3 操作:询问
。
思想
如果没有 2 操作就是吉老师线段树裸题。
但是加上了 2 操作就变得十分棘手。
实际上我们并不要求复杂度一定是
我们仍旧使用线段树,但是每个节点维护一个 multiset。
维护当前节点取过 max 的数值和原本的值。
这样撤销只要在 multiset 里删除相应
询问就返回 multiset 里最大的一个。
取 max 就在 multiset 里加入
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int MAXN = 8e5 + 5;
struct SegTree {
multiset<int, greater<>> body[MAXN];
int ls[MAXN], rs[MAXN];
void build(int k, int l, int r) {
ls[k] = l;
rs[k] = r;
body[k].clear();
if (l == r)
return;
int lc = k * 2;
int rc = k * 2 + 1;
int mid = l + r >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
void update(int k, int l, int r, int x) {
if (l <= ls[k] && rs[k] <= r) {
body[k].insert(x);
return;
}
int mid = ls[k] + rs[k] >> 1;
int lc = k * 2;
int rc = k * 2 + 1;
if (r <= mid)
update(lc, l, r, x);
else if (l > mid)
update(rc, l, r, x);
else {
update(lc, l, mid, x);
update(rc, mid + 1, r, x);
}
}
void cancel(int k, int l, int r, int x) {
if (l <= ls[k] && rs[k] <= r) {
body[k].erase(body[k].find(x));
return;
}
int mid = ls[k] + rs[k] >> 1;
int lc = k * 2;
int rc = k * 2 + 1;
if (r <= mid)
cancel(lc, l, r, x);
else if (l > mid)
cancel(rc, l, r, x);
else {
cancel(lc, l, mid, x);
cancel(rc, mid + 1, r, x);
}
}
int query(int k, int x) {
if (ls[k] == rs[k])
return *body[k].begin();
int ans = 0;
if (body[k].size())
ans = *body[k].begin();
int mid = ls[k] + rs[k] >> 1;
int lc = k * 2;
int rc = k * 2 + 1;
if (x <= mid)
ans = max(ans, query(lc, x));
else
ans = max(ans, query(rc, x));
return ans;
}
} segtree;
struct Query {
int l, r, x;
} a[MAXN];
int n, q;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
segtree.build(1, 1, n);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
segtree.update(1, i, i, x);
}
cin >> q;
for (int i = 1; i <= q; i++) {
int opt, x;
cin >> opt;
if (opt == 1) {
cin >> a[i].l >> a[i].r >> a[i].x;
segtree.update(1, a[i].l, a[i].r, a[i].x);
} else if (opt == 2) {
cin >> x;
segtree.cancel(1, a[x].l, a[x].r, a[x].x);
} else {
cin >> x;
cout << segtree.query(1, x) << endl;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!