vp 2023 icpc 合肥 解题补题记录 [F E J G]
gym 链接: https://codeforces.com/gym/104857
F. Colorful Balloons
血签, 用 map 存一下每个颜色气球出现的次数, 找出出现次数大于一半的颜色.
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
signed main() {
    int n;
    cin >> n;
    string s;
    map<string, int>ma;
    for (int i = 1; i <= n; i++) {
        cin >> s;
        ma[s]++;
    }
    for (auto c : ma) {
        if (c.second * 2 > n) {
            cout << c.first << endl;
            return 0;
        }
    }
    cout << "uh-oh" << endl;
}
E. Matrix Distances
把每个颜色的坐标存到一个集合里, 在把 x, y 轴分开存, 打一个前缀和, 就能快速求所有的曼哈顿距离
#define rep(i,a,b) for (int i = a; i <= b; ++i)
#define per(i,a,b) for (int i = a; i <= b; --i)
int n, m, c;
unordered_map<int, vector<int> >xma, yma;
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    rep(i, 1, n) {
        rep(j, 1, m) {
            cin >> c;
            xma[c].emplace_back(i);
            yma[c].emplace_back(j);
        }
    }
    for (auto &[clo, v] : xma)sort(v.begin(), v.end());
    for (auto &[clo, v] : yma)sort(v.begin(), v.end());
    long long ans = 0;
    for (auto &[clo, v] : xma) {
        long long pre = 0, cnt = 0;
        for (auto &c : v) {
            cnt++;
            pre += c;
            ans += cnt * c - pre;
        }
    }
    for (auto &[clo, v] : yma) {
        long long pre = 0, cnt = 0;
        for (auto &c : v) {
            cnt++;
            pre += c;
            ans += cnt * c - pre;
        }
    }
    cout << ans * 2 << endl;
}
J. Takeout Delivering
先用 djs 跑出来 1 到每个点路径上最大边的最小值, 再跑一下 n 到每个点路径上最大边的最小值.
再枚举每个边作为 1 到 n 路径上的最大边, 往两边看, 次大边是否符合, 并更新答案.
#define rep(i,a,b) for (int i = a; i <= b; ++i)
#define per(i,a,b) for (int i = a; i <= b; --i)
#define pii pair<int,int>
const int Maxn = 3e5 + 10;
vector<pii>eg[Maxn];
int Maxeg1[Maxn], Maxeg2[Maxn];
int n, m, v, u, w, vis[Maxn];
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> m;
    rep(i, 1, m) {
        cin >> v >> u >> w;
        eg[v].emplace_back(u, w);
        eg[u].emplace_back(v, w);
    }
    rep(i, 2, n) Maxeg1[i] = INT_MAX;
    rep(i, 1, n - 1) Maxeg2[i] = INT_MAX;
    priority_queue<pii,vector<pii>, greater<pii> >q;
    q.emplace(0, 1);
    while (!q.empty()) {
        auto [Maxeg, tis] = q.top();
        q.pop();
        if (vis[tis])continue;
        vis[tis] = 1;
        Maxeg = Maxeg1[tis];
        for (auto [to, w] : eg[tis]) {
            if (max(w, Maxeg) < Maxeg1[to]) {
                Maxeg1[to] = max(w, Maxeg);
                q.emplace(Maxeg1[to], to);
            }
        }
    }
    q = priority_queue<pii,vector<pii>, greater<pii> >();
    q.emplace(0, n);
    rep(i, 1, n)vis[i] = 0;
    while (!q.empty()) {
        auto [Maxeg, tis] = q.top();
        q.pop();
        if (vis[tis])continue;
        vis[tis] = 1;
        Maxeg = Maxeg2[tis];
        for (auto [to, w] : eg[tis]) {
            if (max(w, Maxeg) < Maxeg2[to]) {
                Maxeg2[to] = max(w, Maxeg);
                q.emplace(Maxeg2[to], to);
            }
        }
    }
    int ans = INT_MAX;
    rep(i, 1, n) {
        for (auto [to, w] : eg[i]) {
            if (Maxeg1[i] > w || Maxeg2[to] > w)continue;
            ans = min(ans, w + max(Maxeg1[i], Maxeg2[to]));
        }
    }
    cout << ans << endl;
}
G. Streak Manipulation
二分答案找第 k 长的串的最大长度, check 函数内用 dp.
DP思路: 对于当前的 m i d mid mid, 设 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1] 为前 i i i 个字符,有 j j j 段长度大于等于 m i d mid mid 的连续 1 1 1 串,且当前字符是否位于第 j j j 段连续 1 1 1 串的末尾(0为假1为真)所需的最少操作次数. 转移方式见代码.
int dp[200020][10][2], n, m, k, pre[200010];
signed main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n >> m >> k;
    string s;
    cin >> s;
    s = ' ' + s;
    for (int i = 1; i <= n; ++i) {
        pre[i] = pre[i - 1];
        if (s[i] == '0')pre[i]++;
    }
    auto check = [&](int len) -> bool{
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= k; ++j) {
                dp[i][j][0] = INT_MAX / 2;
                dp[i][j][1] = INT_MAX / 2;
            }
        }
        dp[0][0][0] = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j <= k; ++j) {
                if (s[i] == '0') {
                    dp[i][j][0] = min(dp[i][j][0], min(dp[i - 1][j][0], dp[i - 1][j][1]));
                    dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1] + 1);
                }
                else {
                    dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0]);
                    dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j][1]);
                }
                if (i >= len && j > 0) {
                    dp[i][j][1] = min(dp[i][j][1], dp[i - len][j - 1][0] + pre[i] - pre[i - len]);
                }
            }
        }
        return  min(dp[n][k][0], dp[n][k][1]) <= m;
    };
    int l = 0, r = n;
    while (l < r) {
        int mid = (l + r + 1) >> 1;
        if (check(mid)) {
            l = mid;
        }
        else {
            r = mid - 1;
        }
    }
    if (!l)l = -1;
    cout << l << endl;
}
