Codeforces Round 1023 (Div. 2)
A. LRC and VIP
题意:求能不能把\(a\)数组分成两部分,使得它们的\(gcd\)不相同。
考虑\(gcd\)是小于等于这些数的,那么我们可以把最大值和非最大值分开。无解的情况就是所有数相同。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int max = *std::max_element(a.begin(), a.end());
if (std::ranges::count(a, max) == n) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
if (a[i] == max) {
std::cout << 2 << " \n"[i == n - 1];
} else {
std::cout << 1 << " \n"[i == n - 1];
}
}
}
}
B. Apples in Boxes
题意:\(n\)个数,两个人博弈。每轮一个人选择使得一个\(a_i\)减一。如果操作后\(max - min\)大于\(k\)则数。如果所有\(a_i\)都是零那么则无法操作,无法操作的人输。求第一个人能不能赢。
为了保证极差小于等于\(k\)。减最小值肯定不优。那么不减最小值就只能减其它数,随着其它数变小,极差也只会变小。所以如果一开始第一个人不输,则会一直到所有数都变成零才结束,此时如果总和是奇数第一个人就赢。一开始就输的情况就是最大值减一后极差大于\(k\),或者最大值有多个且极差大于\(k\)。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int max = *std::max_element(a.begin(), a.end());
int min = *std::min_element(a.begin(), a.end());
int cnt = std::ranges::count(a, max);
i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
if (max - 1 - min > k || (max - min > k && cnt > 1) || sum % 2 == 0) {
std::cout << "Jerry\n";
} else {
std::cout << "Tom\n";
}
}
C. Maximum Subarray Sum
题意:给你数组\(a\),有些地方没填。你需要填上后使得最大子段和等于\(k\)。
设\(inf = 1e18\)。
先把没填的地方填上\(-inf\)。然后如果数组的最大子段和大于\(k\),那么无解。
否则我们可以通过填一个地方,其它保持是\(-inf\)来达成目标。如果\(i\)没填,上一个没填的地方是\(pre\),下一个填的地方是\(suf\)。那么我们填了\(i\)后,可能会更改\([pre+ 1, suf - 1]\)的最大子段和。发现随着我们填的数越大,最大子段和是单调不减的,那么可以二分最小的填上后最大子段和大于等于\(k\)的值。最后检查是不是等于\(k\),是就找到了解。记得特判全部都填的情况下最大子段和是不是等于\(k\)。
点击查看代码
void solve() {
int n;
i64 k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
const i64 inf = 1e18;
for (int i = 0; i < n; ++ i) {
if (s[i] == '0') {
a[i] = -inf;
}
}
std::vector<int> suf(n);
suf[n - 1] = n;
for (int i = n - 2; i >= 0; -- i) {
suf[i] = suf[i + 1];
if (s[i + 1] == '0') {
suf[i] = i + 1;
}
}
auto check = [&](int l, int r) -> i64 {
i64 max = 0, sum = 0;
for (int i = l; i <= r; ++ i) {
sum = std::max(0ll, sum + a[i]);
max = std::max(max, sum);
}
return max;
};
if (check(0, n - 1) > k) {
std::cout << "NO\n";
return;
}
for (int i = 0, pre = -1; i < n; ++ i) {
if (s[i] == '0') {
int l = pre + 1, r = suf[i] - 1;
i64 lo = -inf, hi = inf;
while (lo < hi) {
i64 mid = lo + hi >> 1ll;
a[i] = mid;
if (check(l, r) >= k) {
hi = mid;
} else {
lo = mid + 1;
}
}
a[i] = lo;
if (check(l, r) == k) {
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
std::cout << a[i] << " \n"[i == n - 1];
}
return;
}
a[i] = -inf;
pre = i;
}
}
if (check(0, n - 1) != k) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
std::cout << a[i] << " \n"[i == n - 1];
}
}
}
D. Apple Tree Traversing
题意:给你一棵树。每次选择一条路径,路径端点为\(u, v\)。这条路径不能包含选择过的点。如果把\(cnt, u, v\)三个整数加入答案,\(cnt\)是路径上的点数,然后把路径上的点标记为选择过。求字典序最大的答案。
赛时思路正确。但调了一个小时没调出来,代码写的复杂把自己弄红温了。
我们应该先选择一条树的直径,如果有多条直径,选择一个端点最大的。那么把直径删除后把树分成了一棵森林,这些树互不影响,递归用同样的做法做就行。
细节就是每次把这棵子树的点存下来,不要遍历所有点,否则会超时。
时间复杂度大概是\(O(n\sqrt{n})\),类似长链剖分。
点击查看代码
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<int> d(n), st(n), fa(n, -1);
std::vector<std::array<int, 3>> vec;
auto work = [&](auto & self, int root) -> void {
std::vector<int> a;
auto dfs = [&](auto & self, int u) -> void {
a.push_back(u);
for (auto & v : adj[u]) {
if (st[v] || fa[u] == v) {
continue;
}
fa[v] = u;
d[v] = d[u] + 1;
self(self, v);
}
};
d[root] = 0;
fa[root] = -1;
dfs(dfs, root);
for (auto & x : a) {
if (d[x] > d[root] || (d[x] == d[root] && x > root)) {
root = x;
}
}
a.clear();
d[root] = 0;
fa[root] = -1;
dfs(dfs, root);
int u = root;
for (auto & x : a) {
if (d[x] > d[u] || (d[x] == d[u] && x > u)) {
u = x;
}
}
vec.push_back(std::array<int, 3>{d[u] + 1, std::max(root, u), std::min(root, u)});
for (int i = u; u != -1; u = fa[u]) {
st[u] = 1;
}
for (auto & x : a) {
if (!st[x]) {
self(self, x);
}
}
};
work(work, 0);
std::ranges::sort(vec, std::greater<>());
std::vector<int> ans;
for (auto & [x, y, z] : vec) {
ans.push_back(x);
ans.push_back(y + 1);
ans.push_back(z + 1);
}
int m = ans.size();
for (int i = 0; i < m; ++ i) {
std::cout << ans[i] << " \n"[i == m - 1];
}
}