The 2nd Universal Cup. Stage 12- Hefei

发布时间 2023-12-06 22:04:53作者: PHarr

E. Matrix Distances

因为行列的贡献是独立的,所以可以按照颜色分别统计

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    unordered_map<int, vi> cnt;
    for (int i = 1, c; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> c;
            cnt[c].push_back(i);
            cnt[-c].push_back(j);
        }
    }
    int res = 0;
    for (auto &[k, v]: cnt) {
        sort(v.begin(), v.end());
        for (int sum = 0, cnt = 0; auto i: v)
            res += cnt * i - sum, cnt++, sum += i;
    }
    cout << res * 2 << "\n";
    return 0;
}s

F. Colorful Balloons

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    map<string, int> cnt;
    string s;
    for (int i = 1; i <= n; i++) cin >> s, cnt[s]++;

    for (auto [k, v]: cnt) {
        if (v * 2 > n) {
            cout << k << "\n";
            return 0;
        }
    }
    cout << "uh-oh\n";
    return 0;
}

G. Streak Manipulation

可以二分答案,check 可以 dp

二分的第\(k\) 长的段长度是\(x\),则至少有\(k\)个段长度大于等\(x\)

dp 状态为\(f[i][j][0/1]\)表示前\(i\)位,\(j\)个大于等于\(x\)段且第\(i\)\(0/1\)的最小操作次数

因为只能把 0 变为 1,如果要改变最后一段的最优解最后一段的长度就是刚好\(x\),注意的是最后一段前一个位置必须是 0,否则无法转移,转移的代价就是最后一段内0的个数。

#include<bits/stdc++.h>

using namespace std;

#define int long long
const int inf = 1e18;

using vi = vector<int>;
using i32 = int32_t;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    cin >> n >> m >> k;
    string s;
    cin >> s;
    s = ' ' + s;
    vi pre(n + 1);
    for (int i = 1; i <= n; ++i)pre[i] = pre[i - 1] + (int) (s[i] == '0');

    auto check = [&](int x) {
        vector f(n + 1, vector(k + 1, vi(2, inf)));
        f[0][0][0] = f[0][0][1] = 0;
        int res = inf;
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '0') f[i][0][0] = 0, f[i][0][1] = 1;
            else f[i][0][1] = 0;
            for (int j = 1; j <= k; ++j) {
                f[i][j][0] = min(f[i - 1][j][0], f[i - 1][j][1]);
                if (i - x >= 0 and j - 1 >= 0)
                    if (s[i - x] != '1') f[i][j][1] = f[i - x][j - 1][0] + (pre[i] - pre[i - x]);
            }
            res = min({res, f[i][k][0], f[i][k][1]});
        }
        return res <= m;
    };
    int l = 1, r = n, res = -1;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    cout << res << "\n";
    return 0;
}

下面的思路基本相同,但是可以省掉最后一维度。

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    string s;
    cin >> n >> m >> k >> s;
    s = " " + s;
    vi sum(n + 1);
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (s[i] == '0');
    auto check = [=](int x) {
        vector f(n + 1, vi(k + 1, inf));
        f[0][0] = 0;
        for (int i = 1, val; i <= n; i++) {
            f[i] = f[i - 1];
            if (i >= x and s[i - x] != '1') {
                val = sum[i] - sum[i - x];
                for (int j = 1; j <= k; j++)
                    f[i][j] = min(f[i][j], f[max(0ll, i - x - 1)][j - 1] + val);
            }
        }
        return f[n][k] <= m;
    };

    int l = 1, r = n, res = -1;
    for (int mid; l <= r;) {
        mid = (l + r) / 2;
        if (check(mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    cout << res << "\n";
    return 0;
}

J. Takeout Delivering

首先我们可以用 dij 求出从1到每个点路径上的最大边,和从n 到每个点路径上的最大边。然后枚举每一条边\((u,v,w)\)做最大边,则第二长的边一定出现在\((1,u)\)\((v,n)\)之间

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 2e9+5;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<array<int, 3>> edge(m);
    vector<vector<pii>> e(n + 1);
    for (auto &[u, v, w]: edge) {
        cin >> u >> v >> w;
        e[u].emplace_back(v, w);
        e[v].emplace_back(u, w);
    }
    auto dij = [e, n](int x) {
        priority_queue<pii, vector<pii>, greater<pii>> q;
        vi dis(n + 1, inf);
        vector<bool> vis(n + 1);
        dis[x] = 0, q.emplace(0, x);
        while (not q.empty()) {
            auto [d, u] = q.top();
            q.pop();
            if (vis[u]) continue;
            vis[u] = 1;
            for (auto [v, w]: e[u]) {
                if (vis[v] or max(d, w) >= dis[v]) continue;
                dis[v] = max(d, w), q.emplace(dis[v], v);
            }
        }
        return dis;
    };
    auto d1 = dij(1), dn = dij(n);
    int res = inf;
    for (auto [u, v, w]: edge) {
        if (max(d1[u], dn[v]) <= w)
            res = min(res, w + max(d1[u], dn[v]));
        if (max(dn[u], d1[v]) <= w)
            res = min(res, w + max(dn[u], d1[v]));
    }
    cout << res << "\n";
    return 0;
}

E. Matrix Distances

因为行列的贡献是独立的,所以可以按照颜色分别统计

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    unordered_map<int, vi> cnt;
    for (int i = 1, c; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> c;
            cnt[c].push_back(i);
            cnt[-c].push_back(j);
        }
    }
    int res = 0;
    for (auto &[k, v]: cnt) {
        sort(v.begin(), v.end());
        for (int sum = 0, cnt = 0; auto i: v)
            res += cnt * i - sum, cnt++, sum += i;
    }
    cout << res * 2 << "\n";
    return 0;
}s

F. Colorful Balloons

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    map<string, int> cnt;
    string s;
    for (int i = 1; i <= n; i++) cin >> s, cnt[s]++;

    for (auto [k, v]: cnt) {
        if (v * 2 > n) {
            cout << k << "\n";
            return 0;
        }
    }
    cout << "uh-oh\n";
    return 0;
}

G. Streak Manipulation

可以二分答案,check 可以 dp

二分的第\(k\) 长的段长度是\(x\),则至少有\(k\)个段长度大于等\(x\)

dp 状态为\(f[i][j][0/1]\)表示前\(i\)位,\(j\)个大于等于\(x\)段且第\(i\)\(0/1\)的最小操作次数

因为只能把 0 变为 1,如果要改变最后一段的最优解最后一段的长度就是刚好\(x\),注意的是最后一段前一个位置必须是 0,否则无法转移,转移的代价就是最后一段内0的个数。

#include<bits/stdc++.h>

using namespace std;

#define int long long
const int inf = 1e18;

using vi = vector<int>;
using i32 = int32_t;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    cin >> n >> m >> k;
    string s;
    cin >> s;
    s = ' ' + s;
    vi pre(n + 1);
    for (int i = 1; i <= n; ++i)pre[i] = pre[i - 1] + (int) (s[i] == '0');

    auto check = [&](int x) {
        vector f(n + 1, vector(k + 1, vi(2, inf)));
        f[0][0][0] = f[0][0][1] = 0;
        int res = inf;
        for (int i = 1; i <= n; ++i) {
            if (s[i] == '0') f[i][0][0] = 0, f[i][0][1] = 1;
            else f[i][0][1] = 0;
            for (int j = 1; j <= k; ++j) {
                f[i][j][0] = min(f[i - 1][j][0], f[i - 1][j][1]);
                if (i - x >= 0 and j - 1 >= 0)
                    if (s[i - x] != '1') f[i][j][1] = f[i - x][j - 1][0] + (pre[i] - pre[i - x]);
            }
            res = min({res, f[i][k][0], f[i][k][1]});
        }
        return res <= m;
    };
    int l = 1, r = n, res = -1;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    cout << res << "\n";
    return 0;
}

下面的思路基本相同,但是可以省掉最后一维度。

#include <bits/stdc++.h>

using namespace std;

#define int long long
using i32 = int32_t;
using vi = vector<int>;
const int inf = 1e18;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, k;
    string s;
    cin >> n >> m >> k >> s;
    s = " " + s;
    vi sum(n + 1);
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (s[i] == '0');
    auto check = [=](int x) {
        vector f(n + 1, vi(k + 1, inf));
        f[0][0] = 0;
        for (int i = 1, val; i <= n; i++) {
            f[i] = f[i - 1];
            if (i >= x and s[i - x] != '1') {
                val = sum[i] - sum[i - x];
                for (int j = 1; j <= k; j++)
                    f[i][j] = min(f[i][j], f[max(0ll, i - x - 1)][j - 1] + val);
            }
        }
        return f[n][k] <= m;
    };

    int l = 1, r = n, res = -1;
    for (int mid; l <= r;) {
        mid = (l + r) / 2;
        if (check(mid)) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    cout << res << "\n";
    return 0;
}

J. Takeout Delivering

首先我们可以用 dij 求出从1到每个点路径上的最大边,和从n 到每个点路径上的最大边。然后枚举每一条边\((u,v,w)\)做最大边,则第二长的边一定出现在\((1,u)\)\((v,n)\)之间

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using vi = vector<int>;
using pii = pair<int, int>;
const int inf = 2e9+5;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<array<int, 3>> edge(m);
    vector<vector<pii>> e(n + 1);
    for (auto &[u, v, w]: edge) {
        cin >> u >> v >> w;
        e[u].emplace_back(v, w);
        e[v].emplace_back(u, w);
    }
    auto dij = [e, n](int x) {
        priority_queue<pii, vector<pii>, greater<pii>> q;
        vi dis(n + 1, inf);
        vector<bool> vis(n + 1);
        dis[x] = 0, q.emplace(0, x);
        while (not q.empty()) {
            auto [d, u] = q.top();
            q.pop();
            if (vis[u]) continue;
            vis[u] = 1;
            for (auto [v, w]: e[u]) {
                if (vis[v] or max(d, w) >= dis[v]) continue;
                dis[v] = max(d, w), q.emplace(dis[v], v);
            }
        }
        return dis;
    };
    auto d1 = dij(1), dn = dij(n);
    int res = inf;
    for (auto [u, v, w]: edge) {
        if (max(d1[u], dn[v]) <= w)
            res = min(res, w + max(d1[u], dn[v]));
        if (max(dn[u], d1[v]) <= w)
            res = min(res, w + max(dn[u], d1[v]));
    }
    cout << res << "\n";
    return 0;
}