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;
}
posted @ 2025-07-12 22:27  maburb  阅读(188)  评论(0)    收藏  举报