Codeforces Global Round 30 (Div. 1 + Div. 2)
A. Sequence Game
题意:一个数组\(a\),每次选择两个相邻的数,用它们之间的一个值替换它们两个。求最后能不能使得留下的数是\(x\)。
如果\(\min(a) \leq x \leq \max(a)\)则可行。
最小值和最大值和别人操作时可以留下自己,然后让这两个值最后一起操作。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int x;
std::cin >> x;
if (x <= std::ranges::max(a) && x >= std::ranges::min(a)) {
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;
}
B. Even Modulo Pair
题意:给你一个严格递增的序列,求有没有两个数\(x, y\)满足\(y \mod x\)是偶数。其中\(y > x\)。
如果至少有两个偶数,那么偶数模偶数是偶数。
否则考虑一堆奇数的情况,如果\(a_i\)模\(a_{i-1}\)是奇数,设\(a_i = qa_{i-1} + r\),其中\(r\)是奇数,那么\(q\)是偶数,否则奇数乘奇数加奇数会是一个偶数。然后如果\(q = 0\)则\(r = a_i - a_{i-1}\),\(r\)应该是偶数,所以这个情况不成立。那么也就是说\(a_i > 2a_{i-1}\)。
那么当\(n\)很大时,必然有解,且直接双层循环暴力很快就能找到解。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int even = -1;
for (int i = 0; i < n; ++ i) {
if (a[i] % 2 == 0) {
if (even != -1) {
std::cout << even << " " << a[i] << "\n";
return;
} else {
even = a[i];
for (int j = 0; j < i; ++ j) {
if (a[i] % a[j] % 2 == 0) {
std::cout << a[j] << " " << a[i] << "\n";
return;
}
}
}
} else {
for (int j = i + 1; j < n; ++ j) {
if (a[j] % a[i] % 2 == 0) {
std::cout << a[i] << " " << a[j] << "\n";
return;
}
}
}
}
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;
}
C. Dungeon
题意:有\(n\)把剑,每把攻击力为\(a_i\)。有\(m\)个怪物,两个属性\(b_i, c_i\)。如果你用\(i\)这把剑攻击\(j\)这个怪物,需要满足\(a_i \geq b_j\)才可以。且当\(c_j > 0\)时,\(a_i\)会变成\(\max(a_i, c_j)\),否则第\(i\)把剑不能再用。每个怪物也只能杀一次。求最多杀几个。
考虑把\(c_i = 0\)的怪物留到最后杀。
我们希望尽可能提高剑的攻击力,那么可以把剑从小到大排序,怪物按\(b_i\)也从小到大排序,每次选一个大于等于\(b_i\)最小的剑出来,其它更小的剑可以留着杀\(c_i = 0\)的怪物。那么每次最小的剑可能变大,相当于用一个大剑换了一个小剑。最后把所有剑从小到大贪心攻击\(c_i = 0\)的怪物就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n), b(m), c(m);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < m; ++ i) {
std::cin >> b[i];
}
std::vector<std::pair<int, int>> d(m);
for (int i = 0; i < m; ++ i) {
std::cin >> c[i];
d[i] = {b[i], c[i]};
}
std::ranges::sort(d);
std::multiset<int> s(a.begin(), a.end());
int ans = 0;
std::vector<int> e;
std::multiset<int> s1;
for (auto & [x, y] : d) {
while (s.size() && *s.begin() < x) {
s1.insert(*s.begin());
s.erase(s.begin());
}
if (s.size()) {
auto it = s.begin();
int v = *it;
if (y != 0) {
++ ans;
s.erase(it);
v = std::max(v, y);
s.insert(v);
} else {
e.push_back(x);
}
} else {
break;
}
}
s.insert(s1.begin(), s1.end());
for (auto & x : e) {
while (s.size() && *s.begin() < x) {
s.erase(s.begin());
}
if (s.size()) {
++ ans;
s.erase(s.begin());
} else {
break;
}
}
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;
}
D. Copy String
题意:两个字符串\(s, t\),你想要把\(s\)变成\(t\)。每一轮操作力,你可以让\(s'_i = s_i\)或\(s_{i-1}\)。然后\(s = s'\)。求最小操作数和方案。如果最小操作数大于\(k_max\)输出\(-1\)。
显然操作只能把前面的字符蔓延到后面。我们可以从后往前看,每次找\(s_j = t_i\)的最后的\(j\),这个\(j\)不能超过后面选择的位置。
具体说,我们记录一个\(last\),表示\(t_{i+1}\)选择的位置,那么我们需要移动\(i+1 - last\)步把\(s_last\)处的字符蔓延到\(t_{i+1}\)。这个过程肯定会覆盖\(t_i\)一次。所以\(t_i\)选择的位置必须小于等于\(last\)。如果没有这样的位置无解。
然后我们可以按照每个位置选择的位置,每次操作中把还没到位的往后移就行。操作数就是最远的两个位置。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, K;
std::cin >> n >> K;
std::string s, t;
std::cin >> s >> t;
if (s[0] != t[0]) {
std::cout << -1 << "\n";
return;
}
std::array<std::vector<int>, 26> pos;
std::vector<int> p(n), d(n);
int k = 0;
for (int i = 0; i < n; ++ i) {
pos[s[i] - 'a'].push_back(i);
}
int last = n;
for (int i = n - 1; i >= 0; -- i) {
auto it = std::ranges::upper_bound(pos[t[i] - 'a'], std::min(i, last));
if (it == pos[t[i] - 'a'].begin()) {
std::cout << -1 << "\n";
return;
}
-- it;
p[i] = *it;
d[i] = i - p[i];
last = std::min(last, p[i]);
k = std::max(k, d[i]);
}
if (k > K) {
std::cout << -1 << "\n";
return;
}
std::cout << k << "\n";
for (int i = 1; i <= k; ++ i) {
std::string ns = s;
for (int j = 1; j < n; ++ j) {
if (d[j] >= i) {
ns[j] = s[j - 1];
}
}
std::cout << ns << "\n";
s = ns;
}
}
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;
}
E. Journey
题意:一个\(n\)个点\(m\)条边的无向图。每条边有边权。你从\(1\)出发,需要经过每条边恰好一次,代价为这条边的边权。你还有一个传送操作,对于点\(u, v\),从\(u\)传送到\(v\)的代价为其一条路径上编号最大的边的边权。路径不一定是简单路径,你可以经过一个点或一条边多次。最后你需要回到\(1\)点。求最小代价。
由于每条边必须经过一次,所以答案至少为所有边的边权和。我们需要考虑怎样使用传送操作,使得增加的代价最小。
要求经过每一条边并且最后回到起点,可以想到欧拉回路。在欧拉回路中,要求每个点的度数为偶数。现在有传送操作,意味着我们可以在奇数点之间进行传送,也就是我们需要把奇数点两两匹配。
考虑\(kruskal\)按编号从小到大枚举边,考虑一条边连接两个连通块,如果从这两个连通块匹配两个点的话,就需要经过这条边,那么可以把这条边看作一个新点,连接这两个连通块的根节点。这样做之后,叶子节点就恰好是原图里的\(n\)个点,其它非叶子节点都是一条边。并且每个点父节点的编号大于子节点的编号。那么对于这棵树的一个非叶子节点,将其两个子树的点两两匹配,可以得到的最小代价为这个点和它所有祖先节点的最小值。
那么在这棵树上\(dfs\),贪心匹配就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> fa, cnt;
DSU();
DSU(int n) {
init(n);
}
void init(int n) {
fa.assign(n + 1, 0);
cnt.assign(n + 1, 1);
std::ranges::iota(fa, 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
int u = find(x), v = find(y);
if (u == v) {
return false;
}
fa[v] = u;
cnt[u] += cnt[v];
return true;
}
bool same(int u, int v) {
return find(u) == find(v);
}
int size(int u) {
return cnt[find(u)];
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::tuple<int, int, int>> edges(m);
i64 ans = 0;
std::vector<int> deg(n);
for (int i = 0; i < m; ++ i) {
int u, v, w;
std::cin >> u >> v >> w;
-- u, -- v;
edges[i] = {u, v, w};
deg[u] ^= 1;
deg[v] ^= 1;
ans += w;
}
const int inf = 1e9;
std::vector<std::vector<int>> adj(2 * n);
std::vector<int> w1(2 * n, inf);
DSU dsu(2 * n);
int N = n;
for (int i = 0; i < m; ++ i) {
auto & [u, v, w] = edges[i];
int x = dsu.find(u);
int y = dsu.find(v);
if (x == y) {
w1[x] = std::min(w1[x], w);
continue;
}
dsu.merge(x, y);
dsu.merge(N, x);
adj[N].push_back(x);
adj[N].push_back(y);
w1[N ++ ] = w;
}
int root = dsu.find(0);
std::vector<int> cnt(2 * n);
auto dfs = [&](auto && self, int u, int min) -> void {
if (u < n) {
cnt[u] = deg[u];
return;
}
min = std::min(w1[u], min);
int x = adj[u][0], y = adj[u][1];
self(self, y, min);
self(self, x, min);
int t = std::min(cnt[x], cnt[y]);
cnt[x] -= t;
cnt[y] -= t;
ans += (i64)min * t;
cnt[u] = cnt[x] + cnt[y];
};
dfs(dfs, root, inf);
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号