VP Educational Codeforces Round 180 (Rated for Div. 2)


A. Race

题意:\(Alice\)\(a\)点,宝物可能出现在\(x\)\(y\)点,判断有没有一个位置使得不管宝物在哪你都可以比\(Alice\)提前拿到。

\(x \leq a \leq y\)无解,否则只需要视情况放在\(x\)\(y\)上就比\(Alice\)快。

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

using i64 = long long;

void solve() {
	int a, x, y;
	std::cin >> a >> x >> y;
	if (x > y) {
		std::swap(x, y);
	}
	if (a >= x && a <= y) {
		std::cout << "NO\n";	
	} else {
		std::cout << "YES\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. Shrinking Array

题意:一个数组有两个相邻的数差小于等于1就是好的。你每次可以选择两个相邻的数,然后用他们之间的数替换这两个数,求最少几次操作使得数组是好的。

首先判断是不是一开始就满足条件,如果不是,则看是不是递增或者递减的,如果是则无解。
否则只需要一次操作:一定有三个相邻数\(a, b, c\),则如果\(a\)是三者中最大值,那么\(b\)一定是最小值,因为如果\(a > b > c\)是递减的,而我们不是这种情况,则\(c \in [b, a]\),一次操作就可以满足。其它情况也是同理。

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

using i64 = long long;

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

	bool f1 = false, f2 = true, f3 = true;
	for (int i = 1; i < n; ++ i) {
		f1 |= std::abs(a[i] - a[i - 1]) <= 1;
		f2 &= a[i] > a[i - 1];
		f3 &= a[i] < a[i - 1];
	}

	if (f1) {
		std::cout << 0 << "\n";
	} else if (f2 || f3) {
		std::cout << -1 << "\n";
	} else {
		std::cout << 1 << "\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. Coloring Game

题意:一个数组\(Alice\)拿三个数,然后\(Bob\)任意拿一个数(可以拿\(Alice\)手上的数)。求有多少种方法使得\(Alice\)的总和一定大于\(Bob\)

如果\(Bob\)\(Alcie\)手中的数,那么需要满足\(Alice\)任意两个数的和大于第三个数,如果拿之外的数,则\(Alice\)的总和要大于最大值。

那么可以排序后,双层循环枚举最小值和最大值,然后二分中间值最少是多少。

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

using i64 = long long;

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

	std::ranges::sort(a);

	int max = a.back();

	i64 ans = 0;	
	for (int i = 0; i < n; ++ i) {
		for (int j = i + 2; j < n; ++ j) {
			int l = i + 1, r = j - 1;
			while (l < r) {
				int mid = l + r >> 1;
				if (a[i] + a[mid] + a[j] <= max || a[i] + a[mid] <= a[j]) {
					l = mid + 1;
				} else {
					r = mid;
				}
			}

			if (a[i] + a[l] + a[j] > max && a[i] + a[l] > a[j]) {
				ans += j - 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;
}

D. Reachability and Tree

题意:给你\(n\)的点的一棵树,你需要给每条边指定方向,使得树上正好有\(n\)条路径。

一条边不管方向都会贡献一条路径,那么已经有了\(n-1\)条边。我们尝试构造正好\(n-1\)条边,发现选一个点作为根然后按层的奇偶性指定边的方向就可以构造出来。此时我们想改变一些边的方向使得多一条路径。我们可以选一个点,让它的一个子节点和它已经它的父节点构造一个长度为2的路径,可以方向这个点的度数正好为\(2\),如果大于\(2\)一定多构造一些路径。那么我们只需要以这个点为根,按上述方法构造就行,也就是一个子树奇数层正向偶数层反向,一个子树奇数层反向偶数层正向。

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

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::vector<std::vector<int>> adj(n);
	for (int i = 1; i < n; ++ i) {
		int u, v;
		std::cin >> u >> v;
		-- u, -- v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	std::vector<std::pair<int, int>> ans;
	auto dfs = [&](auto & self, int u, int d, int fa) -> void {
		for (auto & v : adj[u]) {
			if (v == fa) {
				continue;
			}

			if (d == 0) {
				ans.emplace_back(u, v);
			} else {
				ans.emplace_back(v, u);
			}
			self(self, v, d ^ 1, u);
		}
	};

	for (int i = 0; i < n; ++ i) {
		if (adj[i].size() == 2) {
			ans.emplace_back(adj[i][0], i);
			ans.emplace_back(i, adj[i][1]);
			dfs(dfs, adj[i][0], 0, i);
			dfs(dfs, adj[i][1], 1, i);
			break;
		}
	}

	if (ans.size() == 0) {
		std::cout << "NO\n";
	} else {
		std::cout << "YES\n";
		for (auto & [u, v] : ans) {
			std::cout << u + 1 << " " << v + 1 << "\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;
}
posted @ 2025-06-27 16:12  maburb  阅读(166)  评论(0)    收藏  举报