Educational Codeforces Round 157 (Rated for Div. 2)
A. Treasure Chest
就是看钥匙和宝箱的位置,由于拿钥匙不消耗体力,因此如果先拿钥匙或者同时拿到钥匙和宝箱是可以直接打开宝箱的。
但是如果先拿到宝箱一定是先拿宝箱走一段最优,这样后面取回钥匙来能够少走一段路。
时间复杂度 O ( t ) O(t) O(t)
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int x, y, k;
std::cin >> x >> y >> k;
int ans = 0;
if (y <= x) {
ans = x;
} else {
if (y - x <= k) {
ans = y;
} else {
ans = 2 * y - x - k;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
B. Points and Minimum Distance
其实就是把 2 ∗ n 2*n 2∗n个点划分为两个集合,使得集合内部相邻两个数的差值的和最小。
所以其实只要排个序,让相邻的两个数相减就好了。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
n *= 2;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int ans = 0;
std::vector<std::pair<int, int>> res(n / 2);
std::sort(a.begin(), a.end());
for (int i = 0; i < n / 2; i++) {
res[i] = {a[i], a[i + n / 2]};
}
for (int i = 1; i < n / 2; i++) {
ans += res[i].first - res[i - 1].first + res[i].second - res[i - 1].second;
}
std::cout << ans << "\n";
for (auto [x, y] : res) {
std::cout << x << " " << y << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
C. Torn Lucky Ticket
这道题实在没想到怎么做。
按照长度来存储字符串,然后枚举这两个长度的字符串看是否满足情况。
由于当前正枚举的长度 x , y x,y x,y已经确定,记 h = ( x + y ) / 2 h=(x+y)/2 h=(x+y)/2,所以实际上是可以确定哪一部分字符属于左半边,哪一部分字符属于右半边。
对于左边长度小于 h h h的部分,权加上当前数位;当左边长度大于 h h h的部分,权减去当前数位。
这样做的原因其实就是,左边部分减去是和右边部分加上是等价的,对于右边的部分也类似。
时间复杂度 O ( 25 n ) O(25n) O(25n)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::array<std::vector<std::string>, 6> f;
for (int i = 0; i < n; i++) {
std::string s;
std::cin >> s;
int len = s.size();
f[len].push_back(s);
}
i64 ans = 0;
for (int x = 1; x <= 5; x++) {
for (int y = 1 ; y <= 5; y++) {
if ((x + y) % 2 == 0) {
std::array<int, 100> cnt{};
int h = (x + y) / 2;
for (auto s : f[x]) {
int mask = 50;
for (int i = 0; i < x; i++) {
if (i < h) {
mask += s[i] - '0';
} else {
mask -= s[i] - '0';
}
}
cnt[mask] += 1;
}
for (auto s : f[y]) {
int mask = 50;
for (int i = 0; i < y; i++) {
if (i + x >= h) {
mask += s[i] - '0';
} else {
mask -= s[i] - '0';
}
}
ans += cnt[mask];
}
}
}
}
std::cout << ans << "\n";
return 0;
}
D. XOR Construction
本来以为是个构造,但其实就是一个贪心。
对于这类涉及位运算的题目,首先一个就是应该考虑拆位,这个大家应该是都能够想到的。
其实是异或操作,异或其实是可以相消的,把 b i ⊕ b i + 1 = a i b_i \oplus b_{i + 1} = a_i bi⊕bi+1=ai的式子全部异或起来就会发现 b 1 ⊕ b j + 1 = ⊕ k = 1 j a k b_1 \oplus b_{j+1} = \oplus_{k = 1}^{j}a_k b1⊕bj+1=⊕k=1jak,等价于 b j + 1 = ⊕ k = 1 j a k ⊕ b 1 b_{j+1} = \oplus_{k = 1}^{j}a_k \oplus b_1 bj+1=⊕k=1jak⊕b1
也就是说只要确定了第一项,那么后面的每一项就都可以确定了。
由于题目限制了 b i b_i bi是从 0 0 0到 n − 1 n-1 n−1的,那么其实我们需要让所有的 b i b_i bi尽可能小。
因此我们只要做一个
a
i
a_i
ai的异或前缀和然后考虑每一位上
a
i
a_i
ai的0和1的个数以确定
b
1
b_1
b1当前这一位应该是0还是1就好了。
时间复杂度
O
(
31
n
)
O(31n)
O(31n)
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n - 1; i++) {
int x;
std::cin >> x;
a[i + 1] = a[i] ^ x;
}
int ans = 0;
for (int i = 30; i >= 0; i--) {
int res = 0;
for (int j = 1; j < n; j++) {
res += a[j] >> i & 1;
}
if (n <= 2 * res) {
ans |= 1 << i;
}
}
std::cout << ans << " ";
for (int j = 1; j < n; j++) {
std::cout << (ans ^ a[j]) << " \n"[j == n];
}
return 0;
}