Codeforces Round 1043 (Div. 3)


A. Homework

按题意模拟。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m;
	std::string a, b, c;
	std::cin >> n >> a >> m >> b >> c;
	int l = 0, r = m - 1;
	for (int i = 0; i < m; ++ i) {
		if (c[i] == 'V') {
			a = b[i] + a;
		} else {
			a = a + b[i];
		}
	}
	std::cout << a << "\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. The Secret Number

题意:一个数\(x\)在某尾添加了若干个\(0\)后变成\(y\),给出\(n = x + y\)。求所有合法的\(x\)

\(n\)显然等于\((10^k + 1)x\)
枚举即可。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	i64 n;
	std::cin >> n;
	std::vector<i64> ans;
	for (__int128 i = 10; i < n; i *= 10) {
		if (n % (i + 1) == 0) {
			ans.push_back(n / (i + 1));
		}
	}
	std::ranges::sort(ans);
	std::cout << ans.size() << "\n";
	for (auto & x : ans) {
		std::cout << x << " \n"[x == ans.back()];
	}
}

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;
}

C1. The Cunning Seller (easy version)

题意:第\(i\)个物品体积为\(3^i\),代价为\(3^{i+1} + i3^{i-1}\), 每个物品有无限个。求恰好装满容量为\(n\)的背包的方案中,选择最少的物品的方案里代价最小的。

因为要选择最少的物品,那么肯定是按体积从大到小选。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	i64 n;
	std::cin >> n;
	std::vector<std::pair<i64, i64>> a;
	std::vector<i64> p3;
	for (i64 x = 1; x <= 1e10; x *= 3) {
		p3.push_back(x);
	}
	
	for (int i = 0; i + 1 < p3.size(); ++ i) {
		a.emplace_back(p3[i], p3[i + 1] + i * (i ? p3[i - 1] : 1));
	}
	
	i64 ans = 0;
	for (int i = (int)a.size() - 1; i >= 0; -- i) {
		i64 cnt = n / a[i].first;
		ans += cnt * a[i].second;
		n %= a[i].first;
	}
	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;
}

C2. The Cunning Seller (hard version)

题意:和\(C1\)的区别是,最多选择\(k\)个物品使得体积为\(n\)的最小代价。

发现可以用三个\(i-1\)物品代替一个\(i\)物品。那么先从大到小拿,记录每个物品拿的个数,然后从大到小能换小的就换小的,每次会使得选择的物品增加\(2\)(去掉一个\(i\)添加\(3\)\(i-1\))。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	i64 n, k;
	std::cin >> n >> k;
	std::vector<std::pair<i64, i64>> a;
	std::vector<i64> p3;
	for (i64 x = 1; x <= 1e10; x *= 3) {
		p3.push_back(x);
	}
	
	for (int i = 0; i + 1 < p3.size(); ++ i) {
		a.emplace_back(p3[i], p3[i + 1] + i * (i ? p3[i - 1] : 1));
	}
	
	int m = a.size();
	std::vector<i64> cnt(m);
	for (int i = m - 1; i >= 0; -- i) {
		cnt[i] = n / a[i].first;
		n %= a[i].first;
		k -= cnt[i];
	}

	if (k < 0) {
		std::cout << -1 << "\n";
		return;
	}

	for (int i = m - 1; i > 0; -- i) {
		i64 t = std::min(k / 2, cnt[i]);
		cnt[i] -= t;
		cnt[i - 1] += t * 3;
		k -= t * 2;
	}

	i64 ans = 0;
	for (int i = 0; i < m; ++ i) {
		ans += a[i].second * cnt[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;
}

D. From 1 to Infinity

题意:所有正整数从小到大排列组成一个字符串,截取其前\(k\)个数字,求每一位数字的和。

可以先枚举数字长度,那么长度为\(i\)的数字有\(10^{i+1} - 10^i - 1\)个,如果\(\lfloor \frac{k}{i} \rfloor\)大于这个数,那么长度为\(i\)的数都被包含,\(k\)减少\((10^{i+1} - 10^i - 1)\times i\)。如果小于等于这个数,则可以得到最后一个数了,这个数可能不完整,可以先加上它的贡献。
然后考虑求\([1, n]\)的数位和。可以数位\(dp\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	i64 k;
	std::cin >> k;
	i64 ans = 0, n;
	for (i64 x = 1, i = 1; ; x *= 10, ++ i) {
		i64 l = x, r = x * 10 - 1;
		if (k / i > r - l + 1) {
			k -= (r - l + 1) * i;
		} else {
			i64 t = k / i;
			k -= t * i;
			if (k) {
				std::string s = std::to_string(l + t);
				for (int i = 0; i < k; ++ i) {
					ans += s[i] - '0';
				}
			}
			n = l + t - 1;
			break;
		}
	}

	auto get = [&](i64 n) -> i64 {
		std::vector<int> a;
		while (n) {
			a.push_back(n % 10);
			n /= 10;
		}

		int m = a.size();
		std::vector<i64> f(m, -1), g(m, -1);
		auto dp = [&](auto & self, int u, bool limit, bool zero) -> std::pair<i64, i64> {
			if (u == -1) {
				return {0, 1};
			}

			if (!limit && !zero && f[u] != -1) {
				return {f[u], g[u]};
			}

			int up = limit ? a[u] : 9;
			i64 res = 0, cnt = 0;
			for (int i = 0; i <= up; ++ i) {
				auto [x, y] = self(self, u - 1, limit && i == up, zero && i == 0);
				res += x + i * y;
				cnt += y;
			}

			if (!limit && !zero) {
				f[u] = res;
				g[u] = cnt;
			}
			return {res, cnt};
		};

		return dp(dp, m - 1, true, true).first;
	};

	ans += get(n);

	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. Arithmetics Competition

题意:两个数字\(a, b\)。每次查询给出\(x, y, z\)\(a\)中最多拿出\(x\)个数,\(b\)中最多拿出\(y\)个数,两个数字拿出恰好\(z\)个数。求这些数的和最大是多少。

如果没有\(x, y\)的限制,考虑拿\(z\)个数的最大和,肯定是先把两个数组都从大到小排序,那么这是一个归并两个数字过程,每次拿两个数组开头最大的那一个。那么可以求得拿\(z\)个数是从\(a\)里拿\(X_z\)个数,从\(b\)里拿\(Y_z\)个数。那么对于每个询问,如果\(x \geq X_z\)\(y \geq Y_z\)则直接就是最大值。否则肯定是一个数组拿的数变少了,假设是\(a\),那么多出来的数从\(b\)里拿就行,因为\(a\)肯定就是拿这些数了,不会再减少了。那么预处理前缀和就可以\(O(1)\)回答。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m, q;
	std::cin >> n >> m >> q;
	std::vector<int> a(n), b(m);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	for (int i = 0; i < m; ++ i) {
		std::cin >> b[i];
	}

	std::ranges::sort(a, std::greater<>());
	std::ranges::sort(b, std::greater<>());
	std::vector<i64> suma(n + 1), sumb(m + 1);
	for (int i = 0; i < n; ++ i) {
		suma[i + 1] = suma[i] + a[i];
	}

	for (int i = 0; i < m; ++ i) {
		sumb[i + 1] = sumb[i] + b[i];
	}

	std::vector<int> A(n + m + 1), B(n + m + 1);
	for (int i = 0, j = 0, k = 1; k <= n + m; ++ k) {
		if (j == m || (i < n && a[i] >= b[j])) {
			++ i;
		} else {
			++ j;
		}

		A[k] = i;
		B[k] = j;
	}

	while (q -- ) {
		int x, y, z;
		std::cin >> x >> y >> z;
		if (x >= A[z] && y >= B[z]) {
			std::cout << suma[A[z]] + sumb[B[z]] << "\n";
		} else if (x < A[z]) {
			std::cout << suma[x] + sumb[B[z] + A[z] - x] << "\n";
		} else {
			std::cout << suma[A[z] + B[z] - y] + sumb[y] << "\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. Rada and the Chamomile Valley

题意:一个简单无向图,每次问一个点距离最近的关键边里编号最小的。关键边是指\(1\)\(n\)的任意一条路径都会经过的边。

考虑如何求出关键边,缩点就行了。此时图变成了树,\(1\)\(n\)所在联通分量的路径是唯一的。
然后以距离为第一关键字,答案编号为第二关键字跑\(dijkstra\)
查询直接\(O(1)\)回答就行了。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<std::vector<std::pair<int, int>>> adj(n);
	std::vector<std::pair<int, int>> edges(m);
	for (int i = 0; i < m; ++ i) {
		int u, v;
		std::cin >> u >> v;
		-- u, -- v;
		adj[u].emplace_back(v, i);
		adj[v].emplace_back(u, i);
		edges[i] = {u, v};
	}

	std::vector<int> dfn(n, -1), low(n), bel(n), stk(n);
	int idx = 0, cnt = 0;
	auto tarjan = [&](auto & self, int u, int fa) -> void {
		dfn[u] = low[u] = idx ++ ;
		stk.push_back(u);
		for (auto & [v, id] : adj[u]) {
			if (id == fa) {
				continue;
			}

			if (dfn[v] == -1) {
				self(self, v, id);
				low[u] = std::min(low[u], low[v]);
			} else {
				low[u] = std::min(low[u], dfn[v]);
			}
		}

		if (low[u] == dfn[u]) {
			int v;
			do {
				v = stk.back();
				stk.pop_back();
				bel[v] = cnt;
			} while (v != u);
			++ cnt;
		}
	};

	tarjan(tarjan, 0, -1);

	std::vector<std::vector<std::pair<int, int>>> adj1(cnt);
	for (int i = 0; i < m; ++ i) {
		auto & [u, v] = edges[i];
		if (bel[u] != bel[v]) {
			adj1[bel[u]].emplace_back(bel[v], i);
			adj1[bel[v]].emplace_back(bel[u], i);
		}
	}

	std::set<int> S;
	auto dfs = [&](auto & self, int u, int fa) -> bool {
		if (u == bel[n - 1]) {
			return true;
		}

		for (auto & [v, id] : adj1[u]) {
			if (v == fa) {
				continue;
			}

			if (self(self, v, u)) {
				S.insert(id);
				return true;
			}
		}
		return false;
	};

	dfs(dfs, bel[0], -1); 

	using A = std::tuple<int, int, int>;
	std::priority_queue<A, std::vector<A>, std::greater<A>> heap;
	std::vector<int> dist(n, 1e9), ans(n, -1);
	for (auto & id : S) {
		auto & [u, v] = edges[id];
		if (ans[u] == -1) {
			heap.emplace(0, id + 1, u);
			ans[u] = id + 1;
			dist[u] = 0;
		}

		if (ans[v] == -1) {
			heap.emplace(0, id + 1, v);
			ans[v] = id + 1;
			dist[v] = 0;
		}
	}

	while (heap.size()) {
		auto [d, id, u] = heap.top(); heap.pop();
		if (dist[u] != d) {
			continue;
		}

		for (auto & [v, _] : adj[u]) {
			if (dist[v] > dist[u] + 1 || (dist[v] == dist[u] + 1 && ans[u] < ans[v])) {
				dist[v] = dist[u] + 1;
				ans[v] = ans[u];
				heap.emplace(dist[v], ans[v], v);
			}
		}
	}

	int Q;
	std::cin >> Q;
	while (Q -- ) {
		int c;
		std::cin >> c;
		-- c;
		std::cout << ans[c] << " \n"[!Q];
	}
}

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-08-22 01:00  maburb  阅读(660)  评论(0)    收藏  举报