VP Educational Codeforces Round 43 (Rated for Div. 2)
A. Minimum Binary Number
题意:给你一个01串,你每次可以交换相邻的两个元素,或者把两个相邻的1变成一个1。求二进制表示小最小的数。
显然我们可以把1消除到只剩一个。那么答案就是一个1加原串的所有0.
要特判原串只有一个0的情况。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
if (n == 1) {
std::cout << s << "\n";
return;
}
int cnt = std::ranges::count(s, '0');
std::string ans = "1" + std::string(cnt, '0');
std::cout << ans << "\n";
}
B. Lara Croft and the New Game
题意:\(n\times m\)的矩阵,起点为\((1, 1)\)。按如下规则移动,先移动到\(n, 1\),然后移动到\(n, m\),之后按\(S\)型移动,也就是先移动到\(n - 1, m\),然后向左到\(n - 1, 2\),然后移动到\(n - 2, 2\),然后向右移动到\(n - 2, m\)。如此左右切换。移动一格算一步,求移动\(k\)步后到了哪里。
分两部分判断,一部分是还没有进行\(S\)型移动,也就是\(k \leq n + m - 2\)。
否则求出移动了多少行,也就是\(\lceil \frac{k - (n + m - 2)}{m - 1} \rceil\),根据奇偶性得到接下来向左还是向右,然后剩下的步数就是\((k - (n + m - 2)) \% (m - 1)\)。
点击查看代码
void solve() {
i64 n, m, k;
std::cin >> n >> m >> k;
if (k <= n + m - 2) {
if (k <= n - 1) {
std::cout << 1 + k << " " << 1 << "\n";
} else {
std::cout << n << " " << k - n + 2 << "\n";
}
return;
}
k -= n + m - 2;
int r = (k + m - 2) / (m - 1), c = k % (m - 1) == 0 ? m - 1 : k % (m - 1);
if (r & 1) {
std::cout << n - r << " " << m - c + 1 << "\n";
} else {
std::cout << n - r << " " << 1 + c << "\n";
}
}
C. Nested Segments
题意:给你\(n\)个区间,求两个区间使得一个区间被另一个区间完全包含。
先按右端点排序,那么我们从前往后枚举,前面的区间的右端点都比当前端点小。那么我们只需要中一个左端点比当前大的进行,可以用\(set\)存前面的左端点,然后二分。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::array<int, 3>> a(n);
for (int i = 0; i < n; ++ i) {
int l, r;
std::cin >> l >> r;
a[i] = {r, l, i};
}
std::ranges::sort(a, [&](std::array<int, 3> & a, std::array<int, 3> & b) {
if (a[0] == b[0]) {
return a[1] > b[1];
}
return a[0] < b[0];
});
std::set<std::pair<int, int>> s;
for (auto & [r, l, id] : a) {
auto it = s.lower_bound({l, 0});
if (it != s.end()) {
std::cout << it->second + 1 << " " << id + 1 << "\n";
return;
}
s.insert({l, id});
}
std::cout << -1 << " " << -1 << "\n";
}
D. Degree Set
题意:给你一个递增序列\(d\),你要构造一个有\(d_n + 1\)个点的图,使得这些点的度数集合是\(d\)。
对于一个\(n\)个点的图,如果我们给\([1, i]\)的点给其它点都连边,那么\([1, i]\)的点的度数都是\(n - 1\),其它点的度数是\(i\)。
那么我们可以先给\([1, d_1]\)向\([1, d_n]\)其它点都连边,然后给\([d_1 + 1, d_2]\)的点\([d_1 + 1, d_{n-1}+1]\)的点都连边,给\([d_{i-1} + 1, d_i]\)向\([d_{i} + 1, d_{n-i+1}+1]\)的连边,就正好满足条件。因为第\(i\)部分的每个点有\(n - d_{n-i+1} - 1\)个没和它连边,度数恰好是\(d{n-i+1}\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
int l = 1, r = n;
std::vector<std::pair<int, int>> ans;
while (l <= r) {
for (int i = a[l - 1] + 1; i <= a[l]; ++ i) {
for (int j = i + 1; j <= a[r] + 1; ++ j) {
ans.emplace_back(i, j);
}
}
++ l;
-- r;
}
std::cout << ans.size() << "\n";
for (auto & [u, v] : ans) {
std::cout << u << " " << v << "\n";
}
}
E. Well played!
题意:有\(n\)个人,每个有生命值和攻击力,你可以进行\(a\)次操作把某个人的生命值乘二,可以进行\(b\)次操作把攻击力的值变成生命值的值。问最终攻击力最大和。
我们把第一类操作都给一个人值最优的。因为如果一个人生命值乘二然后赋值给攻击力更优,那么下一次再给他乘二,它也是最优的。
那么我们枚举第一类操作给谁,然后选\(k-1\)个人把生命值赋值给攻击力,这个可以用\(set\)维护。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
k = std::min(n, k);
i64 sum = 0;
std::vector<std::array<int, 3>> a(n);
for (int i = 0; i < n; ++ i) {
int h, d;
std::cin >> h >> d;
a[i] = {h - d, h, d};
sum += d;
}
if (k == 0) {
std::cout << sum << "\n";
return;
}
std::ranges::sort(a, std::greater<>());
std::multiset<int> s;
for (int i = 0; i < k; ++ i) {
if (a[i][0] > 0) {
s.insert(a[i][0]);
sum += a[i][0];
}
}
i64 ans = sum;
for (int i = 0; i < n; ++ i) {
if (a[i][0] > 0 && i < k) {
s.extract(a[i][0]);
sum -= a[i][0];
}
i64 v = (1ll << m) * a[i][1] - a[i][2];
if (s.size() == k) {
ans = std::max(ans, sum - *s.begin() + v);
} else {
ans = std::max(ans, sum + v);
}
if (a[i][0] > 0 && i < k) {
s.insert(a[i][0]);
sum += a[i][0];
}
}
std::cout << ans << "\n";
}
F. Minimal k-covering
题意:给你一个二分图,对于每个\(k, k \in [0, minDegree]\),其中\(minDegree\)是顶点度数的最小值,你要选一些边,使得每个点的度数至少为\(k\),且边数最少。
考虑网络流,如果我们把至少选\(k\)条边当成给每个点\(k\)的流量,是不行的,因为可能需要某个左边点多出边给右边的点,才能使得右边点出\(k\)条边。
我们可以把选至少\(k\)条边,变成最多选\(d_i - k\)条边,其中\(d_i\)是\(i\)度数。那么选出来的边就是不要的边,我们希望不要的边最多,则可以跑网络流。
然后一个经典套路是我们可以利用残余网络,而不是每次都建图重新跑。发现\(k\)的方案是\(k + 1\)的方案减少一些边过来的,那么我们可以从大到小跑网络流,每次给边多加1的流量就行。
点击查看代码
constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
struct _Edge {
int to;
T cap;
_Edge(int to, T cap) : to(to), cap(cap) {}
};
int n;
std::vector<_Edge> e;
std::vector<std::vector<int>> g;
std::vector<int> cur, h;
MaxFlow() {}
MaxFlow(int n) {
init(n);
}
void init(int n) {
this->n = n;
e.clear();
g.assign(n, {});
cur.resize(n);
h.resize(n);
}
bool bfs(int s, int t) {
h.assign(n, -1);
std::queue<int> que;
h[s] = 0;
que.push(s);
while (!que.empty()) {
const int u = que.front();
que.pop();
for (int i : g[u]) {
auto [v, c] = e[i];
if (c > 0 && h[v] == -1) {
h[v] = h[u] + 1;
if (v == t) {
return true;
}
que.push(v);
}
}
}
return false;
}
T dfs(int u, int t, T f) {
if (u == t) {
return f;
}
auto r = f;
for (int &i = cur[u]; i < int(g[u].size()); ++i) {
const int j = g[u][i];
auto [v, c] = e[j];
if (c > 0 && h[v] == h[u] + 1) {
auto a = dfs(v, t, std::min(r, c));
e[j].cap -= a;
e[j ^ 1].cap += a;
r -= a;
if (r == 0) {
return f;
}
}
}
return f - r;
}
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
g[v].push_back(e.size());
e.emplace_back(u, 0);
}
T flow(int s, int t) {
T ans = 0;
while (bfs(s, t)) {
cur.assign(n, 0);
ans += dfs(s, t, std::numeric_limits<T>::max());
}
return ans;
}
std::vector<int> get(int n1, int n2) {
std::vector<int> res;
for (int u = 0; u < n1; ++ u) {
for (auto & i : g[u]) {
if (e[i].to >= n1 && e[i].to < n1 + n2 && e[i].cap != 0) {
res.push_back(i / 2 + 1 - n1 - n2);
}
}
}
return res;
};
void addS(int s, T w) {
for (auto & i : g[s]) {
e[i].cap += w;
}
}
void addT(int t, T w) {
for (auto & i : g[t]) {
e[i ^ 1].cap += w;
}
}
};
void solve() {
int n1, n2, m;
std::cin >> n1 >> n2 >> m;
std::vector<std::pair<int, int>> edges(m);
std::vector<int> d(n1 + n2);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
edges[i] = {u, v};
++ d[u];
++ d[v + n1];
}
int k = m;
for (int i = 0; i < n1 + n2; ++ i) {
k = std::min(k, d[i]);
}
MaxFlow<int> f(n1 + n2 + 2);
int s = n1 + n2, t = n1 + n2 + 1;
for (int i = 0; i < n1; ++ i) {
f.addEdge(s, i, d[i] - k);
}
for (int i = 0; i < n2; ++ i) {
f.addEdge(i + n1, t, d[i + n1] - k);
}
for (auto & [u, v] : edges) {
f.addEdge(u, v + n1, 1);
}
std::vector<std::vector<int>> ans(k + 1);
for (int i = k; i >= 0; -- i) {
f.flow(s, t);
ans[i] = f.get(n1, n2);
f.addS(s, 1); f.addT(t, 1);
}
for (int i = 0; i <= k; ++ i) {
std::cout << ans[i].size();
for (auto & x : ans[i]) {
std::cout << " " << x;
}
std::cout << "\n";
}
}