Codeforces Round 1035 (Div. 2)


A. Add or XOR

题意:你要从\(a\)变成\(b\),每次花费\(x\)代价使得\(a\)加一或者花费\(y\)代价使得\(a = a \oplus 1\)。求最小代价。

偶数异或\(1\)才会有加一效果,且\(y<x\)时才会用异或。数据小可以直接模拟,也可以算出来中间有多少偶数,直接计算。奇数异或\(1\)有减一的效果,如果\(a = b + 1\)可以用。
如果\(a > b\)\(a \oplus 1 \neq b\)无解。

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

using i64 = long long;

void solve() {
	int a, b, x, y;
	std::cin >> a >> b >> x >> y;
	if (a == b + 1 && (a ^ 1) == b) {
		std::cout << y << "\n";
		return;
	}
	if (a > b) {
		std::cout << -1 << "\n";
		return;
	}

	int ans = 0;
	for (int i = a; i < b; ++ i) {
		if (i % 2 == 0 && y < x) {
			ans += y;
		} else {
			ans += x;
		}
	}
	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. Line Segments

题意:平面上两个点,用\(n\)条线段连接,每个线段有\(a_i\)的长度。求能不能恰好从起点连到终点。

我们在两个点的连线上放线段,那么每个线段要么向左要么向右,我们计算出来可以到达的最小距离和最大距离,然后每个线段可以旋转,这样它在连线上的覆盖的距离会减少,每个线段都可以旋转,我们可以猜测最小距离到最大距离直接的距离都可以达到。那么只要两个点的距离在这个范围内就可以连接。
最大距离就是所有线段的和,最小距离我们可以只要看:如果最长的线段比其它线段的和还要长,那么其它点不能和它抵消,至少要走\(max_{len} - (sum_{len} - max_{len})\)的距离。否则还是因为可以旋转,我们总有办法抵消掉,使得下界为\(0\)
可以给距离都平方避免使用浮点数。

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

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	i64 px, py, qx, qy;
	std::cin >> px >> py >> qx >> qy;
	std::vector<i64> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	i64 d = (px - qx) * (px - qx) + (py - qy) * (py - qy);
	i64 sum = std::accumulate(a.begin(), a.end(), 0ll), max = *std::max_element(a.begin(), a.end());
	i64 maxd = sum * sum, mind = 0;
	if (max > sum - max) {
		mind = max - (sum - max);
	}	

	if (mind * mind <= d && d <= maxd) {
		std::cout << "YES\n";
	} else {
		std::cout << "NO\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. A Good Problem

题意:构造一个长度为\(n\)的数组,元素范围都在\([l, r]\)之间,且按位与的和等于按位异或的和。求这样的数组中字典序最小的数组的第\(k\)位。

如果\(l = r\),显然只有\(n\)时奇数有解。\(n\)时奇数的时候任意范围都有解,因为最小字典序显然是\(n\)\(l\)
如果\(n\)是偶数,\(n=2\)无解。否则我们可以尝试构造一个按位与和按位或都是\(0\)的数组。显然希望\(l\)在前面,那么可以找一个数\(x\)\(x \& l = 0\),那么构造\(n-2\)\(l\)\(2\)\(x\)就可以满足条件。设\(l\)二进制最高位为\(i\),如果\(2^{i+1} \leq r\)那么\(x = 2^{i+1}\)。否则无解。

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

using i64 = long long;

void solve() {
	i64 n, l, r, k;
	std::cin >> n >> l >> r >> k;
	if (l == r) {
		if (n & 1) {
			std::cout << l << "\n";
		} else {
			std::cout << -1 << "\n";
		}
		return;
	}

	if (n & 1) {
		std::cout << l << "\n";
	} else {
		if (n == 2) {
			std::cout << -1 << "\n";
			return;
		}
		for (int i = 59; i >= 0; -- i) {
			if (l >> i & 1) {
				if ((1ll << (i + 1)) <= r) {
					if (k <= n - 2) {
						std::cout << l << "\n";
					} else {
						std::cout << (1ll << (i + 1)) << "\n";
					}
					return;
				}
				break;
			}
		}

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

D. Token Removing

题意:长度为\(n\)的数组合法条件为对于\(i \in [1, n]\)\(0 \leq a_i \leq i\)。对于每个数组从前往后操作,如果\(a_i = 0\)不操作,否则在\([a_i, i]\)中选一个没有操作过的位置,数组的价值为有多少不同的操作序列。求所有合法数组的价值和。

重要的一点是需要想出来倒着\(dp\)
\(f[i][j]\)\([i, n]\)操作了\(j\)个位置的价值。
那么如果\(i\)不被\([i, n]\)某个位置操作,则\(f[i][j] += f[i + 1][j]\)
如果我们从\([i, n]\)中选一个位置\(j\)操作\(i\),那么\(a_j \leq i\),一共有\(n - i + 1 - j\)个位置可以选择,那么就是\(f[i][j + 1] += f[i + 1][j] \times i \times (n - i + 1 - j)\)。这样为什么能计算出正确的答案?我们可以想已经有一个后缀组成的序列,现在我们可以在一些位置插入\(i\),显然不同位置插入一个\(i\)就是不同的方案。每个\(a_j\)取值可以是\([1, i]\),不同的\(a_j\)其实对应不同的数组,而在每个数组中都有\(n - i + 1 - j\)个位置插入\(i\)可以产生不同的方案。所以直接相乘就是价值。

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

using i64 = long long;

void solve() {
	int n, m;
    std::cin >> n >> m;
    std::vector<int> f(n + 1);
    f[0] = 1;
    for (int i = n; i >= 1; -- i) {
        std::vector<int> g(n + 1);
        for (int j = 0; j <= n - i; ++ j) {
            g[j] = (g[j] + f[j]) % m;
            g[j + 1] = (g[j + 1] + (i64)f[j] * i % m * (n - i + 1 - j) % m) % m;
        }

        f = g;
    }

    int ans = 0;
    for (int i = 0; i <= n; ++ i) {
        ans = (ans + f[i]) % m;
    }
    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;
}
posted @ 2025-07-06 00:35  maburb  阅读(756)  评论(2)    收藏  举报