Codeforces Round 1026 (Div. 2)
A. Fashionable Array
题意:删除最少的数,使得最大值减最小值是偶数。
\(n\)很小,暴力枚举。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::ranges::sort(a.begin(), a.end());
int ans = n;
for (int i = 0; i < n; ++ i) {
for (int j = i; j < n; ++ j) {
if ((a[i] + a[j]) % 2 == 0) {
ans = std::min(ans, i + n - 1 - j);
}
}
}
std::cout << ans << "\n";
}
B. Down with Brackets
题意:给你一个合法的括号序列,你要恰好删掉一个左括号和一个右括号,判断能不能让序列不平衡。
upd:
如果某个前缀的左右括号个数相等,那么可以删掉这个前缀部分中的一个左括号,然后还要删一个右括号,需要删后面的,判断后面有没有右括号就行。
已更正题解,之前说删右括号找后面的左括号的做法是错的。一个前缀是平衡的,那么这个前缀后面的部分也是平衡的。那么后面的部分肯定又有左括号又有右括号。其实只要判断当前位置是不是最后一个位置就行。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
int p = s.find_last_of(')');
for (int i = 0, cnt = 0; i < p; ++ i) {
if (s[i] == '(') {
++ cnt;
} else {
-- cnt;
}
if (cnt == 0) {
std::cout << "YES\n";
return;
}
}
std::cout << "NO\n";
}
C. Racing
题意:\(h\)从零开始,每次加一或加零,但有些地方需要你来决定。需要满足\(l_i \leq h_i \leq r_i\)。
从左到右模拟,如果不是\(-1\)就加上,否则把这个位置存下来。到了\(h < l_i\)的时候,就把前面的\(-1\)从大到小改成\(1\),把\(h\)改成\(l_i\)。最后模拟一遍判断是不是都合法。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
std::vector<int> l(n + 1), r(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> l[i] >> r[i];
}
std::vector<int> b;
std::vector<int> h(n + 1);
for (int i = 1; i <= n; ++ i) {
h[i] = h[i - 1];
if (a[i] != -1) {
h[i] += a[i];
} else {
b.push_back(i);
}
if (h[i] > r[i]) {
std::cout << -1 << "\n";
return;
}
if (h[i] < l[i]) {
int k = l[i] - h[i];
if (b.size() < k) {
std::cout << -1 << "\n";
return;
}
for (int j = k; j >= 1; -- j) {
int u = b.back(); b.pop_back();
a[u] = 1;
}
h[i] = l[i];
}
}
for (int i = 1, h = 0; i <= n; ++ i) {
if (a[i] == -1) {
a[i] = 0;
}
h += a[i];
if (h < l[i] || h > r[i]) {
std::cout << -1 << "\n";
return;
}
}
for (int i = 1; i <= n; ++ i) {
std::cout << a[i] << " \n"[i == n];
}
}
D. Fewer Batteries
题意:一个有向无环图,边\((u, v)\)有\(u < v\)。每到一个点\(u\)可以增加\([0, b_u]\)的值。对于一条边权为\(w\)的边,想要通过需要值大于等于\(w\)。求从\(1\)到\(n\)结束时最小的值。
显然如果较小的值可以达到\(n\),那么更大的值也可以。那么可以二分答案。
然后进行\(dp\),\(f[1] = \min(mid, b[1])\)。从左到右,每条边\((u, v)\)更新\(f[v]\)的值。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
std::vector<std::vector<std::pair<int, int>>> adj(n);
std::vector<int> in(n);
for (int i = 0; i < m; ++ i) {
int u, v, w;
std::cin >> u >> v >> w;
-- u, -- v;
adj[u].emplace_back(v, w);
}
auto check = [&](int limit) -> bool {
std::vector<int> f(n, -1);
f[0] = std::min(limit, b[0]);
for (int u = 0; u < n; ++ u) {
for (auto & [v, w] : adj[u]) {
if (f[u] >= w) {
f[v] = std::max(f[v], std::min(limit, f[u] + b[v]));
}
}
}
return f[n - 1] != -1;
};
int l = 0, r = 1e9;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
if (!check(l)) {
std::cout << -1 << "\n";
} else {
std::cout << l << "\n";
}
}
E. Melody
题意:每个元素有\(a_i, b_i\)。你要构造一个排列,使得任意相邻的两个元素在\(a, b\)中恰好有一个不同,且没有连续的三个元素的\(a\)或\(b\)都相同。
从\(a_i\)向\(b_i\)无向边。那么选择一个元素相当于选择了一条边,然后选择的下一个点必然对应另一个元素的\(a\)或\(b\),这样就转化为了欧拉路径问题。注意\(a, b\)要分成两部分,相同的\(a,b\)应该视为不同的点,不然会出现自环然后导致有三个连续相同的点。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::pair<int, int>> a(n);
std::vector<int> b, c;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i].first >> a[i].second;
b.push_back(a[i].first);
c.push_back(a[i].second);
}
std::ranges::sort(b);
b.erase(std::unique(b.begin(), b.end()), b.end());
int m1 = b.size();
auto get1 = [&](int x) -> int {
return std::ranges::lower_bound(b, x) - b.begin();
};
std::ranges::sort(c);
c.erase(std::unique(c.begin(), c.end()), c.end());
int m2 = c.size();
auto get2 = [&](int x) -> int {
return std::ranges::lower_bound(c, x) - c.begin();
};
int m = m1 + m2;
std::vector<int> head(m, -1), ver(2 * n), next(2 * n);
int tot = 0;
auto add = [&](int u, int v) -> void {
ver[tot] = v; next[tot] = head[u]; head[u] = tot ++ ;
};
std::vector<int> deg(m);
for (int i = 0; i < n; ++ i) {
auto & [u, v] = a[i];
u = get1(u);
v = get2(v) + m1;
add(u, v);
add(v, u);
++ deg[u]; ++ deg[v];
}
int odd = 0, s = -1;
for (int i = 0; i < m; ++ i) {
if (deg[i] & 1) {
++ odd;
s = i;
}
}
if (odd != 0 && odd != 2) {
std::cout << "NO\n";
return;
}
if (s == -1) {
s = 0;
}
std::vector<int> vis(n * 2);
std::vector<int> ans;
auto euler = [&](auto & self, int u) -> void {
while (head[u] != -1) {
int i = head[u];
while (i != -1 && vis[i]) {
i = next[i];
}
if (i == -1) {
break;
}
vis[i] = vis[i ^ 1] = 1;
head[u] = next[i];
self(self, ver[i]);
ans.push_back(i / 2);
}
};
euler(euler, s);
if (ans.size() != n) {
std::cout << "NO\n";
return;
}
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] + 1 << " \n"[i == n - 1];
}
}