AtCoder Beginner Contest 372

news/2024/9/21 22:24:16
省流版
  • A. 暴力即可
  • B. 转换3进制即可
  • C. 考虑答案的组成,仅修改发生变化的部分即可
  • D. 维护答案数组\(ans_i\),考虑枚举 \(j\)对哪些 \(i\)有贡献,通过单调栈找到对应的区间\(i\) ,通过差分维护区间加法即可
  • E. 并查集维护连通块,\(set\)维护点标号大小,合并\(set\)时启发式合并,查询则从对应连通块的\(set\)最大值往前找\(k\)次即可
  • F. 考虑朴素\(dp\),发现有效转移只有 \(O(mk)\) 个,记忆化搜索即可

A - delete . (abc372 A)

题目大意

给定一个字符串,删除其中的.

解题思路

依次判断每个字符,如果不是.就输出。

python可以一行,

神奇的代码
print(input().replace('.', ''))


B - 3^A (abc372 B)

题目大意

给定一个\(m\),将其表达成若干个 \(3\)的幂的和。

解题思路

其实就是将\(m\)转换成三进制,看三进制下每一个幂的系数。

进制转换一下即可。

神奇的代码
#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 m;cin >> m;vector<int> ans;for (int i = 0; i <= 10; ++i) {int cnt = m % 3;while (cnt--) {ans.push_back(i);}m /= 3;}cout << ans.size() << '\n';for (auto i : ans)cout << i << " ";cout << '\n';return 0;
}


C - Count ABC Again (abc372 C)

题目大意

给定一个字符串\(s\),进行以下\(q\)次操作。

每次操作,将\(s_x = c\)。问操作完后,字符串 ABC\(s\)的出现次数。

解题思路

\(f(i) = 1/0\)表示 \(s_is_{i+1}s_{i+2}\)是/不是 ABC

答案就是\(\sum_{i = 1}^{n} f(i)\)

每次修改,其实只有 \(3\)\(f(i)\)的值可能发生变化。

因此先计算出 \(\sum_{i = 1}^{n} f(i)\),然后对于每次修改,先减去\(f(x-2)+f(x-1)+f(x)\)个 ,然后修改字符后,重新计算\(f(x-2)+f(x-1)+f(x)\)即可得到新的答案,而不需要重新计算 \(\sum_{i = 1}^{n} f(i)\)

时间复杂度就为\(O(q)\)

神奇的代码
#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, q;string s;cin >> n >> q >> s;int ans = 0;auto in_range = [&](int i) { return i >= 0 && i < n; };auto check = [&](int x) {for (int i = 0; i < 3; ++i)if (!in_range(x + i) || s[x + i] != 'A' + i)return false;return true;};for (int i = 0; i < n; ++i) {ans += check(i);}while (q--) {int x;string c;cin >> x >> c;--x;for (int i = x - 2; i <= x; ++i) {ans -= check(i);}s[x] = c[0];for (int i = x - 2; i <= x; ++i) {ans += check(i);}cout << ans << '\n';}return 0;
}


D - Buildings (abc372 D)

题目大意

给定\(n\)个数的数组 \(a\),对于每个 \(i \in [1,n]\) ,问\(j\)的数量满足 \((i,j]\)\(a_j\)是最大值。

解题思路

如果枚举\(i\),求 \(j\),复杂度是 \(O(n^2)\)

反过来,枚举 \(j\),考虑它会对哪些 \(i\)\(1\)的贡献。 会发现只要满足\(\max(a[i+1..j]) \leq j\),那么这个 \(j\)就会对 \(i\)\(1\)的贡献。

即对于每个数\(a_j\),我们找其左边第一个\(a_l > a_j\),那么\(ans[l..j-1] += 1\)

对于第一个,如何找到其左边第一个\(a_l > a_j\)呢?这是一个经典问题,从右往左,用单调栈维护一个递减的数列即可(栈顶到栈底是递减的)。

对于第二个,有若干次区间加法,但最后只有一次查询,可以用差分数组将区间加变成单点修改,最后还原出答案数组即可。

神奇的代码
#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> h(n);for (auto& x : h)cin >> x;stack<int> s;vector<int> diff(n + 1);for (int i = n - 1; i >= 0; i--) {while (!s.empty() && h[s.top()] < h[i]) {diff[i]++;diff[s.top()]--;s.pop();}s.push(i);}while (!s.empty()) {diff[0]++;diff[s.top()]--;s.pop();}int ans = 0;for (int i = 0; i < n; ++i) {ans += diff[i];cout << ans << " \n"[i == n - 1];}return 0;
}


E - K-th Largest Connected Components (abc372 E)

题目大意

给定\(n\)个点,初始无边。维护两类操作。

  • 1 u v,连一条无向边\(u \to v\)
  • 2 v k,找\(v\)所在连通块的第 \(k\)大的点的标号。

\(k \leq 10\)

解题思路

注意到\(k \leq 10\),如果能用 \(set\)维护一个连通块的所有点的标号,那么直接从 rbegin()暴力数\(k\)个就是答案,

如果维护这个 \(set\)呢?连通块的信息自然用并查集维护,当合并两个连通块时,其 \(set\)也要合并,我们可以将小的 \(set\)合并到大的 \(set\)中,即启发式合并,每次合并 \(set\)最坏的复杂度是 \(O(n)\),但最坏情况下,合并后的 \(set\)大小会翻倍,最多翻倍 \(log\)次就达到 \(n\)个点,即最坏情况的出现次数只有 \(O(\log n)\)次,因此启发式合并的时间复杂度是 \(O(n \log n)\)

维护好 \(set\)后,直接从 \(rbegin()\)\(k\)个点即为答案。特判少于\(k\)个点的情况。

时间复杂度是 \(O((n + qk) \log n)\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;class dsu {public:vector<int> p;vector<int> sz;vector<set<int>> node;int n;dsu(int _n) : n(_n) {p.resize(n);sz.resize(n);node.resize(n);iota(p.begin(), p.end(), 0);fill(sz.begin(), sz.end(), 1);for (int i = 0; i < n; i++) {node[i].insert(i);}}inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); }inline bool unite(int x, int y) {x = get(x);y = get(y);if (x != y) {if (sz[x] < sz[y])swap(x, y);node[x].insert(node[y].begin(), node[y].end());p[y] = x;sz[x] += sz[y];return true;}return false;}
};int main(void) {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int n, q;cin >> n >> q;dsu d(n);while (q--) {int op;cin >> op;if (op == 1) {int u, v;cin >> u >> v;--u, --v;d.unite(u, v);} else {int v, k;cin >> v >> k;--v;int fa = d.get(v);if (d.node[fa].size() < k) {cout << -1 << '\n';} else {auto it = d.node[fa].rbegin();advance(it, k - 1);cout << *it + 1 << '\n';}}}return 0;
}


F - Teleporting Takahashi 2 (abc372 F)

题目大意

给定一张有向图,它首先是个环,然后在此基础上加了\(m \leq 50\)条有向边。

问从 \(1\)号点出发,走 \(k\)个点,所形成的点的路径 \((1,v_1,v_2,...,v_k)\)的情况数。

解题思路

考虑朴素搜索,设\(dp[u][k]\)表示从 \(u\)号点走 \(k\)步的方案数,显然其答案就是 \(\sum_{u \to v}dp[v][k-1]\)

但这复杂度是 \(O(nk)\),显然过不了。

但注意到有很多点 \(u \to v\)\(v\)其实只有一个即 \(u+1\),即只有一种走法,它们的转移显然是没必要的。我们可以只考虑有分叉点的 \(u\)。而这有分叉点的 \(u\)最多只有 \(m\)个。因此如果我们的转移是从 $u \to $分叉点,这样的时间复杂度就是 \(O(mk)\), 是可以通过的。

记当前点\(u\),然后找到 \(u\)后第一个分叉点 \(x\), 其出度\(> 1\),那么 \(dp[u][k] = \sum_{x \to y}dp[y][k^\prime]\),转移后\(k^\prime\)可以通过简单的运算得到,即\(k - dis(u \to x) - 1\)

有效的\(u\)就只有 \(2m\)个,对 \(dp\)进行记忆化搜索,复杂度即为 \(O(mk)\)。因为用\(map\)来记忆化会超时,所以只好离散化有效的 \(u\),转成数组来记忆化了。

神奇的代码
#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, m, k;cin >> n >> m >> k;vector<vector<int>> edge(n);for (int i = 0; i < n; i++) {edge[i].push_back((i + 1) % n);}vector<int> tr;for (int i = 0; i < m; i++) {int u, v;cin >> u >> v;u--;v--;edge[u].push_back(v);tr.push_back(u);}for (int i = 0; i < n; i++) {sort(edge[i].begin(), edge[i].end());edge[i].erase(unique(edge[i].begin(), edge[i].end()), edge[i].end());}sort(tr.begin(), tr.end());tr.erase(unique(tr.begin(), tr.end()), tr.end());auto calc = [&](int u, int v) -> int {if (u > v)v += n;return v - u;};int ans = 0;if (m == 0) {ans = 1;} else {vector<int> nxt(n);for (int i = 0; i < n; i++) {auto it = lower_bound(tr.begin(), tr.end(), i);if (it == tr.end())it = tr.begin();nxt[i] = *it;}vector<int> id(n, -1);int cnt = 0;auto dfs_pre = [&](auto& dfs, int u) -> void {if (id[u] != -1)return;id[u] = cnt++;int it = nxt[u];for (int v : edge[it]) {dfs(dfs, v);}};dfs_pre(dfs_pre, 0);vector<vector<int>> dp(k + 1, vector<int>(cnt, -1));auto dfs = [&](auto& dfs, int u, int k) -> int {if (k == 0) {return 1;}if (dp[k][id[u]] != -1)return dp[k][id[u]];int it = nxt[u];int sum = 0;int nxt = (u + 1) % n;int cost = calc(u, it);if (cost >= k)return 1;for (int v : edge[it]) {sum += dfs(dfs, v, k - cost - 1);if (sum >= mo)sum -= mo;}return dp[k][id[u]] = sum;};ans = dfs(dfs, 0, k);}cout << ans << '\n';return 0;
}


G - Ax + By < C (abc372 G)

题目大意

给定三个\(n\)个数的数组\(a,b,c\),问 \((x,y)\)的数量,满足

  • \(a_i x + b_i < c\),对任意 \(i \in [1,n]\)

多组数据。

解题思路

一眼只会\(O(na_i)\)

神奇的代码



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/63122.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

尝试RVC音色克隆团长音色

前言 昨晚玩剑网3突发奇想,把团长声音克隆下来,利用语音喵制作成语音DBM。 这样不管团长开不开团,打团也能有团长声音听了诶嘿嘿。 于是当场关闭游戏声音录了打本的素材,本文就边做边记录。 下载 在B站找到了这个教程: 【你的声音,现在是我的了!】https://www.bilibili.…

隐私保护体系下网络威胁情报共享的研究现状和方案设计

来源:http://netinfo-security.org/article/2024/1671-1122/1671-1122-24-7-1129.shtml威胁情报 网络威胁情报是关于网络中正在进行的或潜在的恶意活动信息,涵盖但不限于特定的恶意软件样本、恶意IP地址、钓鱼电子邮件信息、黑客组织的入侵行为等内容,对于提前感知预警、防范…

Logisim-013-◇汉字显示

转码在线工具地址 https://www.23bei.com/tool/54.html#仓库地址 https://gitee.com/gitliang/logisim-to-cpu

spring6.1在java17环境下使用反射

引包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>3.3.4</version> </dependency> 反射代码编写简单的反射方法,如下所示 package com.lw.reflect.c…

实景三维+耕地保护:构建耕地资源管理的全闭环新模式

在耕地资源日益珍贵的今天,如何高效、精准地实施耕地保护,成为了我国农业可持续发展与生态文明建设的关键课题。“实景三维+耕地保护”的创新模式,能够为这一挑战提供突破性的解决方案,打造一个从前端监测到后端管理的全闭环耕地保护管理模式。本文将深入分析这一模式的核心…

IDEA 如何设置TAB页显示多行

前言 我们在使用IDEA开发时,经常需要打开多个TAB页,但是,IDEA默认的方式是最多只能打开少量的TAB页,且打开的TAB页只能堆积在一行上显示,如果超出了数量,就会自动隐藏。这样对于我能经常需要在多个不同TAB页之间打开来说,是比较麻烦的,那么有什么办法能改变下设置呢? …

在Linux下安装MySQL

摘要 在学习MySQL语法之前,我们需要先解决在Ubuntu或CentOs环境下的“软件安装”的问题。本文梳理了安装前后的各个步骤及有关的注意事项,主要涵盖了安装前的准备工作、如何安装mysql,以及安装之后如何启动、如何正式使用这几个方面。建议读者先浏览一遍,留心相关的注意事项…

深入剖析RocketMQ消息消费原理

本文参考转载至《RocketMQ技术内幕 第2版》一. 消息消费概述 消息消费以组的模式开展,一个消费组可以包含多个消费者,每个消费组可以订阅多个主题,消费组之间有集群模式和广播模式两种消费模式。集群模式是当前主题下的同一条消息只允许被其中一个消费者消费。广播模式是当前…