CFR-835-Div-4解题报告
D. Challenging Valleys
题意:给你一个数组,判断它是否为“山谷形”。
tourist 的做法是假想在最左边和最右边插入一个极大值(结果不变)来统一情况,然后只需要判断下凹(比左右两边都低)的位置个数。如果为 \(1\) 则正确。
By tourist
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int cnt = 0;
int beg = 0;
while (beg < n) {
int end = beg;
while (end + 1 < n && a[end + 1] == a[end]) {
end += 1;
}
if (beg == 0 || a[beg - 1] > a[beg]) {
if (end == n - 1 || a[end + 1] > a[end]) {
cnt += 1;
}
}
beg = end + 1;
}
cout << (cnt == 1 ? "YES" : "NO") << '\n';
}
return 0;
}
E. Binary Inversions
题意:给你一个 01 串,你最多可以将一位取反(也可以不取),问逆序对数最多为多少。
考虑预处理前缀和和后缀和,这样就可以 \(O(1)\) 得到前后缀的 01 个数。先算出总的逆序对个数,然后对于每一位,考虑取反该位的贡献即可。可以通过前后缀 01 个数很简单地求得。
By tourist
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<int> p0(n + 1);
for (int i = 0; i < n; i++) {
p0[i + 1] = p0[i] + (a[i] == 0);
}
vector<int> p1(n + 1);
for (int i = 0; i < n; i++) {
p1[i + 1] = p1[i] + (a[i] == 1);
}
long long inv = 0;
int k1 = 0;
for (int i = 0; i < n; i++) {
if (a[i] == 1) {
k1 += 1;
} else {
inv += k1;
}
}
long long ans = inv;
for (int i = 0; i < n; i++) {
auto cur = inv;
if (a[i] == 0) {
cur -= p1[i];
cur += p0[n] - p0[i + 1];
} else {
cur += p1[i];
cur -= p0[n] - p0[i + 1];
}
ans = max(ans, cur);
}
cout << ans << '\n';
}
return 0;
}
F. Quests
题意:有 \(n\) 中物品,每中物品有一个价值,每种有无穷多个,当你拿走一个物品后 \(k\) 秒不能拿这个物品。问 \(k\) 最多为多少才能在 \(d\) 秒内拿到 \(c\) 的价值。
从大到小排序,二分答案,check 时循环选即可。有一些细节,比如 \(k>n\) 时的处理等。
By tourist
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n, d;
long long c;
cin >> n >> c >> d;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.rbegin(), a.rend());
int low = -1, high = d + 1;
while (low < high) {
int mid = (low + high + 1) >> 1;
long long sum = 0;
for (int i = 0; i < d; i++) {
int id = i % (mid + 1);
if (id < n) {
sum += a[id];
}
}
if (sum >= c) {
low = mid;
} else {
high = mid - 1;
}
}
if (low == d + 1) {
cout << "Infinity" << '\n';
} else {
if (low == -1) {
cout << "Impossible" << '\n';
} else {
cout << low << '\n';
}
}
}
return 0;
}
G. SlavicG's Favorite Problem
题意:有一个无根树,你初始在 \(a\),有一个变量 \(x\) 初始为 \(0\),每经过一条边都会异或上该边的边权。你能到达点 \(b\) 当且仅当 \(x\) 到达 \(b\) 时恰好为 \(0\)。你可以在任意时刻传送到任意位置一次(除了点 \(b\)),问是否能到达点 \(b\)。
可以进行两遍搜索,一遍从 \(a\) 开始但不能走 \(b\),一遍从 \(b\) 开始。判断是否能存在两个点 \(i,j\),使得 \(i\) 到 \(a\) 的距离和 \(j\) 到 \(b\) 的距离相等(此时可以 \(a\to i\Rightarrow j\to b\)。用 set 维护即可。
By tourist
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int tt;
cin >> tt;
while (tt--) {
int n, a, b;
cin >> n >> a >> b;
--a; --b;
vector<vector<pair<int, int>>> g(n);
for (int i = 0; i < n - 1; i++) {
int x, y, w;
cin >> x >> y >> w;
--x; --y;
g[x].emplace_back(y, w);
g[y].emplace_back(x, w);
}
vector<int> d(n, -1);
vector<int> que(1, a);
d[a] = 0;
for (int it = 0; it < (int) que.size(); it++) {
for (auto& p : g[que[it]]) {
int u = p.first;
if (d[u] == -1 && u != b) {
que.push_back(u);
d[u] = d[que[it]] ^ p.second;
}
}
}
set<int> all;
for (int i = 0; i < n; i++) {
if (d[i] != -1) {
all.insert(d[i]);
}
}
d.assign(n, -1);
que.assign(1, b);
d[b] = 0;
for (int it = 0; it < (int) que.size(); it++) {
for (auto& p : g[que[it]]) {
int u = p.first;
if (d[u] == -1) {
que.push_back(u);
d[u] = d[que[it]] ^ p.second;
}
}
}
bool ok = false;
for (int i = 0; i < n; i++) {
if (i != b) {
if (all.find(d[i]) != all.end()) {
ok = true;
break;
}
}
}
cout << (ok ? "YES" : "NO") << '\n';
}
return 0;
}