C. New Skill Acquired

多源bfs

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n;
    cin >> n;
    
    vector<vector<int>> to(n);
    vector<int> got;
    rep(i, n) {
        int a, b;
        cin >> a >> b;
        if (a == 0) {
            got.push_back(i);
        }
        else {
            --a; --b;
            to[a].push_back(i);
            to[b].push_back(i);
        }
    }
    
    vector<bool> used(n);
    queue<int> q;
    for (int v : got) {
        used[v] = true;
        q.push(v);
    }
    
    while (q.size()) {
        int v = q.front(); q.pop();
        for (int u : to[v]) {
            if (used[u]) continue;
            used[u] = true;
            q.push(u);
        }
    }
    
    int ans = 0;
    rep(i, n) if (used[i]) ans++;
    
    cout << ans << '\n';
    
    return 0;
}

D. 2x2 Erasing 2

容易发现答案不超过 \(9\)
那么最多有 \(C(49, 9)\) 种方案,属于极限逼近能过的范围,然后简单剪一下枝就过了

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

void solve() {
    int h, w;
    cin >> h >> w;
    
    vector<string> s(h);
    rep(i, h) cin >> s[i];
    
    int ans = 9;
    auto f = [&](auto& f, int now) -> void {
        if (now >= ans) return;
        rep(i, h-1)rep(j, w-1) {
            int cnt = 0;
            rep(di, 2)rep(dj, 2) if (s[i+di][j+dj] == '#') cnt++;
            if (cnt == 4) {
                rep(dj, 2) {
                    s[i+1][j+dj] = '.';
                    f(f, now+1);
                    s[i+1][j+dj] = '#';
                }
                return;
            }
        }
        
        ans = min(ans, now);
    };
    f(f, 0);
    
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

E. Cut in Half

用大根堆来维护二元组 (值,数量),批量处理相同长度的木棍的切割操作

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

void solve() {
    int n, k, x;
    cin >> n >> k >> x;
    
    using P = pair<double, int>;
    priority_queue<P> q;
    rep(i, n) {
        int a;
        cin >> a;
        q.emplace(a, 1);
    }
    
    while (k) {
        auto [l, c] = q.top(); q.pop();
        if (k < c) {
            q.emplace(l, c-k);
            c = k;
        }
        
        k -= c;
        q.emplace(l/2, c*2);
    }
    
    while (1) {
        auto [l, c] = q.top(); q.pop();
        x -= c;
        if (x <= 0) {
            printf("%.10f\n", l);
            return;
        }
    }
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

F. Adding Chords

性质:

区间 \(I\) 和区间 \(J\) 不相交 \(\Leftrightarrow\) \(I\) 包含 \(J\) 的偶数个交点

考虑一个初值都为 \(0\) 的序列 \(X=(X_1, X_2, \cdots, X_N)\)
每次对点 \(l\) 和点 \(r\) 连一条线段时,就将区间 \([X_l, X_{l+1}, \cdots, X_{r-1}]\) 上每个值加 \(1\)

然后利用上面的性质,只需保证区间 \([l, r)\) 的区间和以及其中的最小值为 \(0\) 就能添加一条线段 \((l, r)\)
可以用线段树来实现

实际上还有一种更简单的做法,就是每次对区间加一个哈希值,这样就不需要再判定区间最小值是否为 \(0\)
可以用树状数组来实现

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ull = unsigned long long;

int main() {
    random_device seed_gen;
    mt19937_64 rnd(seed_gen());
    
    int n, q;
    cin >> n >> q;
    
    fenwick_tree<ull> d(n+1);
    rep(qi, q) {
        int l, r;
        cin >> l >> r;
        
        if (d.sum(0, l) == d.sum(0, r)) {
            puts("Yes");
            ull x = rnd();
            d.add(l, x); d.add(r, -x);
        }
        else puts("No");
    }
    
    return 0;
}

G. Set list

最小割
先将歌曲按 \(b\) 值降序排好
定义 dp[i][k][s] 表示考虑前 \(i\) 首歌曲时,已选 \(k\) 首歌且使用 \(s\) 人时的最大活跃度

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

inline void chmax(ll& a, ll b) { if (a < b) a = b; }

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(n);
    vector<pair<int, int>> bc(m);
    rep(i, n) cin >> a[i];
    rep(i, m) cin >> bc[i].first >> bc[i].second;
    
    ranges::sort(a);
    ranges::sort(bc, greater<>());
    
    const int INF = 1001001001;
    vector<int> ub(m+1, INF);
    rep(i, m+1) {
        int sa = 0;
        for (int x = 1; x <= n; ++x) {
            sa += a[x-1];
            ub[i] = min(ub[i], (n-x)*i + sa);
        }
    }
    
    int mx = n*m+1;
    vector dp(m+1, vector<ll>(mx, -1e18));
    dp[0][0] = 0;
    rep(i, m) {
        auto [b, c] = bc[i];
        for (int j = m-1; j >= 0; --j) {
            rep(k, mx) if (dp[j][k] >= 0) {
                int nj = j+1, nk = k+b;
                if (ub[nj] < nk) continue;
                chmax(dp[nj][nk], dp[j][k]+c);
            }
        }
    }
    
    ll ans = 0;
    rep(i, m+1)rep(j, mx) chmax(ans, dp[i][j]);
    
    cout << ans << '\n';
    
    return 0;
}