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

浙公网安备 33010602011771号