Mirrativ Programming Contest 2025 (AtCoder Beginner Contest 414)
A - Streamer Takahashi
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, l, r;
std::cin >> n >> l >> r;
int ans = 0;
for (int i = 0; i < n; ++ i) {
int x, y;
std::cin >> x >> y;
ans += x <= l && y >= r;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
B - String Too Long
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::string s;
for (int i = 0; i < n; ++ i) {
char c;
int l;
std::cin >> c >> l;
if (s.size() + l > 100) {
std::cout << "Too Long\n";
return;
}
s += std::string(l, c);
}
std::cout << s << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
C - Palindromic in Both Bases
题意:求\([1, n]\)有多少数在\(10\)进制和\(n\)进制下都是回文。
\(n \leq 10^{12}\)。枚举到\(1e6\)。然后把自己复制翻转复制一份到后面,以及把\(0\)到\(9\)插在中间的数都试一下。注意复制的数需要大于\(1e6\)不然会算重。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
i64 A, n;
std::cin >> A >> n;
int N = std::min<i64>(n, 1000000);
auto check = [&](i64 x) -> bool {
std::string s = std::to_string(x);
for (int l = 0, r = (int)s.size() - 1; l < r; ++ l, -- r) {
if (s[l] != s[r]) {
return false;
}
}
std::vector<int> t;
while (x) {
t.push_back(x % A);
x /= A;
} while (x);
for (int l = 0, r = (int)t.size() - 1; l < r; ++ l, -- r) {
if (t[l] != t[r]) {
return false;
}
}
return true;
};
i64 ans = 0;
auto get = [&](const std::string & s) -> i64 {
i64 res = 0;
for (auto & c : s) {
res = res * 10 + c - '0';
}
return res;
};
std::set<i64> set;
for (int i = 1; i <= N; ++ i) {
if (check(i)) {
ans += i;
}
std::string a = std::to_string(i);
auto b = a;
std::ranges::reverse(b);
std::string s = a + b;
i64 j = get(s);
if (j <= n) {
if (j > N && check(j)) {
ans += j;
}
for (int k = 0; k <= 9; ++ k) {
s = a + (char)('0' + k) + b;
j = get(s);
if (j <= n) {
if (j > N && check(j)) {
ans += j;
}
} else {
break;
}
}
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
D - Transmission Mission
题意:给出\(n\)个站点的位置,你需要建\(m\)个电台,每个电台的信号强度需要设置为一个整数\(x\),那么和它距离小于等于\(\frac{x}{2}\)的都可以有信号。求所有站点都有信号的最小信号强度和。
如果一个电台需要包括\([l, r]\)这个范围,那么显然应该建在最中间,而且信号强度为\(r-l\)。
先给站点排序。
题意可以转换为把数组分成\(m-1\)段,每段代价为最大值减最小值。
这个问题如何解决?如果一开始没分段,那么代价是\(a[n] - a[1]\)。如果在\(j\)处分段,那么代价变为\(a[j] - a[1] + a[n] - a[j + 1]\)。发现减少了\(a[j + 1] - a[j]\)。那么把所有\(a[i] - a[i - 1]\)从大到小排序,选前\(m-1\)大的减去。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
m -= 1;
std::ranges::sort(a);
i64 ans = a.back() - a[0];
std::vector<i64> b;
for (int i = 0; i + 1 < n; ++ i) {
b.push_back(a[i + 1] - a[i]);
}
std::ranges::sort(b, std::greater<>());
for (int i = 0; i < m; ++ i) {
ans -= b[i];
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
E - Count A%B=C
题意:求\((a, b, c)\)的数量,满足\(a, b, c \in [1, n]\)且它们互不相同。
显然有\(c < b\)。那么如果\(a < b\)则有\(a = c\)。不满足条件。
如果\(a > b\),那么只有\(b | a\)时\(c = 0\)不合法,否则可以产生合法的三元组。那么对于\(b = i\),有\(n - \lfloor \frac{n}{i} \rfloor - (i - 1)\)的贡献。总贡献为\(\sum_{i=1}^{n} n - \lfloor \frac{n}{i} \rfloor - (i - 1)\)。\(n, (i-1)\)直接算,\(\lfloor \frac{n}{i} \rfloor\)是整除分块板子。
代码省略取模类。
点击查看代码
using Z = MInt<P>;
void solve() {
i64 n;
std::cin >> n;
Z ans = (Z)n * n - (Z)n * (n - 1) / 2;
for (i64 l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans -= (Z)(r - l + 1) * (n / l);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
F - Jump Traveling
题意:一棵树,从根出发,每次走\(k\)条边的距离。求每个点最少走几次可以到达。
一个暴力做法是,记\(dist[u][t][fa]\)为到点\(i\)时步数模\(k\)为\(t\)且上一个节点时\(fa\)的最短路。只有在\(t + 1 == k\)转移时权值才为\(1\)。但这样复杂度太高,无法通过。
我们可以记录边,我们存边时其实存的是两条有向边,那么可以给他们两两编号,这是一个基础技巧,$(0, 1), (2, 3) .. $这样一对一对编号,那么同一条边的正向反向边编号异或为\(1\)。于是可以记\(st[t][id]\)表示走到编号为\(id\)的边距离模\(k\)等于\(t\)的状态是否出现过。然后记\((u, d, fa)\)为点\(u\)步数为\(d\)上一条边编号为\(id\),一开始把\((0, 0, -1)\)入队(点的编号从\(0\)开始)。那么如果\(d \% k == 0\),就走了\(\frac{d}{k}\)次。然后一个优化是,记\(cnt[i][u]\)表示到\(u\)距离模\(k\)为\(i\)出现的次数,显然最多出现两次,一次把其它边都转移,第二次另一条边把当前边转移,于是这个点的所有比就都被转移过了,于是超过两次就\(continue\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<std::vector<std::pair<int, int>>> adj(n);
for (int i = 0; i + 1 < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].emplace_back(v, i * 2);
adj[v].emplace_back(u, i * 2 + 1);
}
const int inf = 1e9;
std::queue<std::tuple<int, int, int>> q;
std::vector st(k, std::vector<int>(2 * n - 2));
std::vector cnt(k, std::vector<int>(n));
std::vector<int> ans(n, -1);
q.emplace(0, 0, -1);
while (q.size()) {
auto [d, u, fa] = q.front(); q.pop();
if (d % k == 0 && ans[u] == -1) {
ans[u] = d / k;
}
if ( ++ cnt[d % k][u] > 2) {
continue;
}
for (auto & [v, id] : adj[u]) {
if ((fa ^ id) == 1 && d % k != 0) {
continue;
}
if (!st[(d + 1) % k][id]) {
st[(d + 1) % k][id] = 1;
q.emplace(d + 1, v, id);
}
}
}
for (int i = 1; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}