接着剤の精進日記

競プロでの精進や研究に関係したことを書いていきます。

AtCoder Beginner Contest 323(ABC323)

atcoder.jp

A - Weak Beats

for文で偶数番目のbitを調べる

提出コード

void solve() {
    string S;
    cin >> S;
    bool even = true;
    for (int i = 1; i < (int)S.size(); i += 2)
        if (S[i] != '0') even = false;
    cout << (even ? "Yes" : "No") << endl;
}

B - Round-Robin Tournament

勝ち数とインデックスをpair型で持ちソートをする

提出コード

void solve() {
    int N;
    cin >> N;
    vector<string> S(N);
    vector<int> cnt(N);
    REP(i, N) {
        cin >> S[i];
        for (char c : S[i])
            if (c == 'o') cnt[i]++;
    }
    vector<pair<int, int>> vp;
    REP(i, N) vp.emplace_back(cnt[i], -i);
    stable_sort(ALL(vp), greater<pair<int, int>>());
    for (auto [_, i] : vp)
        cout << -i + 1 << " ";
    cout << endl;
}

C - World Tour Finals

予め $ A $ を降順にソートしておき、その状態で各人のスコア計算をしておく
各人のスコアをmultisetなどで持っておき、自分のスコアを削除した状態でまだ解いていない $ A_i $ を順に加算し、自分が最高のスコアになるかどうかをチェックする

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    vector<int> A(M);
    REP(i, M) cin >> A[i];
    vector<int> idx(M);
    iota(ALL(idx), 0);
    sort(ALL(idx), [&](int i, int j) { return A[i] > A[j]; });
    vector<string> S(N);
    REP(i, N) cin >> S[i];
    multiset<int> mst;
    REP(i, N) {
        int score = i + 1;
        REP(j, M) {
            if (S[i][j] == 'o') {
                score += A[j];
            }
        }
        mst.insert(score);
    }
    REP(i, N) {
        int score = i + 1;
        REP(j, M) {
            if (S[i][j] == 'o') {
                score += A[j];
            }
        }
        mst.erase(mst.find(score));
        if (*mst.rbegin() < score) {
            cout << 0 << endl;
        }
        else {
            int nxt_score = score;
            int cnt = 0;
            REP(j, M) {
                if (S[i][idx[j]] == 'x') {
                    nxt_score += A[idx[j]];
                    cnt++;
                }
                if (*mst.rbegin() < nxt_score) {
                    break;
                }
            }
            cout << cnt << endl;
        }
        mst.insert(score);
    }
}

D - Merge Slimes

一番小さいものは合成できるだけするのが良く、それを繰り返していけば良い
mapなどで各サイズのスライムが何匹いるかを管理し、最小のものから順に操作をしていけば良い

提出コード

void solve() {
    int N;
    cin >> N;
    vector<ll> S(N), C(N);
    map<ll, ll> mp;
    set<ll> st;
    REP(i, N) {
        cin >> S[i] >> C[i];
        mp[S[i]] += C[i];
        st.insert(S[i]);
    }
    ll ans = 0;
    while (!st.empty()) {
        ll s = *st.begin();
        ll c = mp[s];
        ans += c % 2;
        st.erase(s);
        if (c / 2 > 0) {
            mp[2 * s] += c / 2;
            st.insert(2 * s);
        }
    }
    cout << ans << endl;
}

E - Playlist

$ dp[i][j] := i $ 秒後に曲1が流れている $ (j = 0) $、曲1以外が流れている $ (j = 1) $ 確率としてDPをする

提出コード

void solve() {
    int N, X;
    cin >> N >> X;
    vector<int> T(N);
    REP(i, N) cin >> T[i];
    vector dp(X + 2, vector<mint>(2, 0));
    dp[0][0] = 1;
    REP(i, X + 1) {
        dp[min(i + T[0], X + 1)][0] += dp[i][0] / N;
        dp[min(i + T[0], X + 1)][0] += dp[i][1] / N;
        REP(j, 1, N) {
            dp[min(i + T[j], X + 1)][1] += dp[i][0] / N;
            dp[min(i + T[j], X + 1)][1] += dp[i][1] / N;
        }
    }
    cout << dp[X + 1][0] << endl;
}

F - Push and Carry

箱を上下方向から先に押すか、左右方向から先に押すかを全探索する
最初に箱の上下左右に移動する必要があるが、箱を避けて通る必要があるので予め箱からマンハッタン距離が $ 2 $ の距離に移動して、箱の上下左右に移動することを全探索する
箱の上下左右へ移動する距離はBFSなどで箱を中心とした $ 5 \times 5 $ 程度のグリッド上で求めれば良い
上記で求めた答えのうち最小のものを出力すれば良い

提出コード

void solve() {
    ll XA, YA, XB, YB, XC, YC;
    cin >> XA >> YA >> XB >> YB >> XC >> YC;
    ll ans = LINF;
    for (int i = -2; i <= 2; i++) {
        for (int j = -2; j <= 2; j++) {
            if (abs(i) + abs(j) != 2) continue;
            vector<string> s(5, string(5, '.'));
            s[2][2] = 'x';
            auto bfs = [&](int h, int w) {
                vector<vector<int>> dist(5, vector<int>(5, INF));
                queue<P> q;
                q.push({h, w});
                dist[h][w] = 0;
                while (!q.empty()) {
                    auto [cur_h, cur_w] = q.front();
                    q.pop();
                    REP(k, 4) {
                        int nh = cur_h + dy[k], nw = cur_w + dx[k];
                        if (nh < 0 || nh >= 5 || nw < 0 || nw >= 5) continue;
                        if (s[nh][nw] == 'x') continue;
                        if (dist[nh][nw] <= dist[cur_h][cur_w] + 1) continue;
                        dist[nh][nw] = dist[cur_h][cur_w] + 1;
                        q.push({nh, nw});
                    }
                }
                return dist;
            };
            auto dist = bfs(2 + i, 2 + j);
            // 上から下
            if (YB + i > YB and YB > YC) {
                ll tmp = abs(XA - (XB + j)) + abs(YA - (YB + i));
                tmp += dist[3][2];
                tmp += abs(YB - YC);
                if (XB == XC) chmin(ans, tmp);
                else {
                    tmp += 2;
                    tmp += abs(XC - XB);
                    chmin(ans, tmp);
                }
            }
            // 下から上
            else if (YB + i < YB and YB < YC) {
                ll tmp = abs(XA - (XB + j)) + abs(YA - (YB + i));
                tmp += dist[1][2];
                tmp += abs(YB - YC);
                dumps(tmp, i, j);
                if (XB == XC) chmin(ans, tmp);
                else {
                    tmp += 2;
                    tmp += abs(XC - XB);
                    chmin(ans, tmp);
                }
            }
            // 左から右
            if (XB + j < XB and XB < XC) {
                ll tmp = abs(XA - (XB + j)) + abs(YA - (YB + i));
                tmp += dist[2][1];
                tmp += abs(XB - XC);
                if (YB == YC) chmin(ans, tmp);
                else {
                    tmp += 2;
                    tmp += abs(YC - YB);
                    chmin(ans, tmp);
                }
            }
            // 右から左
            else if (XB + j > XB and XB > XC) {
                ll tmp = abs(XA - (XB + j)) + abs(YA - (YB + i));
                tmp += dist[2][3];
                tmp += abs(XB - XC);
                if (YB == YC) chmin(ans, tmp);
                else {
                    tmp += 2;
                    tmp += abs(YC - YB);
                    chmin(ans, tmp);
                }
            }
        }
    }
    cout << ans << endl;
}

AtCoder Beginner Contest 322(ABC322)

atcoder.jp

A - First ABC 2

全探索をして一番初めに見つかったindexを答える

提出コード

void solve() {
    int N;
    string S;
    cin >> N >> S;
    REP(i, N - 2) {
        if (S.substr(i, 3) == "ABC") {
            cout << i + 1 << endl;
            return;
        }
    }
    cout << -1 << endl;
}

B - Prefix and Suffix

c++20から starts_withends_with が使えるのでそれを使うと楽

提出コード

void solve() {
    int N, M;
    string S, T;
    cin >> N >> M >> S >> T;
    int ans = 3;
    if (T.starts_with(S) and T.ends_with(S)) ans = 0;
    else if (T.starts_with(S)) ans = 1;
    else if (T.ends_with(S)) ans = 2;
    cout << ans << endl;
}

C - Festival

$ i $ 以上となる最小の $ A_j $ を二分探索で求めその差分が答え

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    vector<int> A(M);
    REP(i, M) cin >> A[i];
    REP(i, 1, N + 1) {
        int start = *lower_bound(ALL(A), i);
        cout << start - i << endl;
    }
}

D - Polyomino

ポリオミノの回転、置く場所を全通り試し、条件を満たすかどうかをチェックする

提出コード

void solve() {
    vector<string> P1(4), P2(4), P3(4);
    REP(i, 4) cin >> P1[i];
    REP(i, 4) cin >> P2[i];
    REP(i, 4) cin >> P3[i];
    const int sz = 10;
    auto rot = [](vector<string> &s) -> vector<string> {
        vector<string> res(s[0].size());
        REP(i, s.size()) {
            REP(j, s[0].size()) { res[j].push_back(s[i][j]); }
        }
        REP(i, res.size()) { reverse(res[i].begin(), res[i].end()); }
        return res;
    };
    REP(d1, 4) {
        REP(d2, 4) {
            REP(d3, 4) {
                REP(h1, 7) REP(w1, 7) REP(h2, 7) REP(w2, 7) REP(h3, 7) REP(w3, 7) {
                    vector<vector<char>> fi(10, vector<char>(10, '.'));
                    bool ok = true;
                    REP(i, 4) REP(j, 4) {
                        if (P1[i][j] == '#') {
                            if (3 <= h1 + i and h1 + i <= 6 and 3 <= w1 + j and w1 + j <= 6) fi[h1 + i][w1 + j] = '#';
                            else {
                                ok = false;
                                break;
                            }
                        }
                    }
                    if (!ok) break;
                    REP(i, 4) REP(j, 4) {
                        if (P2[i][j] == '#') {
                            if (3 <= h2 + i and h2 + i <= 6 and 3 <= w2 + j and w2 + j <= 6) {
                                if (fi[h2 + i][w2 + j] == '.') fi[h2 + i][w2 + j] = '#';
                                else {
                                    ok = false;
                                    break;
                                }
                            }
                            else {
                                ok = false;
                                break;
                            }
                        }
                    }
                    if (!ok) break;
                    REP(i, 4) REP(j, 4) {
                        if (P3[i][j] == '#') {
                            if (3 <= h3 + i and h3 + i <= 6 and 3 <= w3 + j and w3 + j <= 6) {
                                if (fi[h3 + i][w3 + j] == '.') fi[h3 + i][w3 + j] = '#';
                                else {
                                    ok = false;
                                    break;
                                }
                            }
                            else {
                                ok = false;
                                break;
                            }
                        }
                    }
                    REP(i, 3, 7) REP(j, 3, 7) if (fi[i][j] != '#') ok = false;
                    if (ok) {
                        cout << "Yes" << endl;
                        dumps(d1, h1, w1, d2, h2, w2, d3, h3, w3);
                        return;
                    }
                }
                P3 = rot(P3);
            }
            P2 = rot(P2);
        }
        P1 = rot(P1);
    }
    cout << "No" << endl;
}

E - Product Development

$ dp[i][k_1][k_2][k_3][k_4][k_5] := i $ 番目まで見てパラメータ $ j $ が $ k_j $ であるときの最小値としてDPを行う

提出コード

ll dp[111][6][6][6][6][6];

void solve() {
    ll N, K, P;
    cin >> N >> K >> P;
    vector<ll> C(N);
    vector<vector<ll>> A(N, vector<ll>(6));
    REP(i, N) {
        cin >> C[i];
        REP(j, K) cin >> A[i][j];
    }
    REP(num, 111) REP(i, 6) REP(j, 6) REP(k, 6) REP(l, 6) REP(m, 6) dp[num][i][j][k][l][m] = LINF;
    dp[0][0][0][0][0][0] = 0;
    REP(num, N) {
        REP(i, 6) REP(j, 6) REP(k, 6) REP(l, 6) REP(m, 6) {
            chmin(dp[num + 1][i][j][k][l][m], dp[num][i][j][k][l][m]);
            chmin(dp[num + 1][min(P, i + A[num][0])][min(P, j + A[num][1])][min(P, k + A[num][2])]
                    [min(P, l + A[num][3])][min(P, m + A[num][4])],
                  dp[num][i][j][k][l][m] + C[num]);
        }
    }
    ll ans = LINF;
    if (K == 1) ans = dp[N][P][0][0][0][0];
    else if (K == 2) ans = dp[N][P][P][0][0][0];
    else if (K == 3) ans = dp[N][P][P][P][0][0];
    else if (K == 4) ans = dp[N][P][P][P][P][0];
    else if (K == 5) ans = dp[N][P][P][P][P][P];
    cout << (ans == LINF ? -1 : ans) << endl;
}

AtCoder Beginner Contest 321(ABC321)

atcoder.jp

A - 321-like Checker

愚直に判定する
文字列で受け取ると少し楽

提出コード

void solve() {
    string N;
    cin >> N;
    bool ok = true;
    REP(i, (int)N.size() - 1) {
        if (N[i] <= N[i + 1]) ok = false;
    }
    cout << (ok ? "Yes" : "No") << endl;
}

B - Cutoff

毎回 $ S $ を生成、ソートしても間に合うので、$ 0 \leq x \leq 100 $ を全探索し条件を満たすかどうかを判定する

提出コード

void solve() {
    string S;
    cin >> S;
    int N = S.size();
    int ans = 1;
    REP(i, N) {
        REP(j, i + 1, N) {
            string t = "";
            REP(k, i, j + 1) t += S[k];
            int sz = t.size();
            bool ok = true;
            REP(k, sz / 2) {
                if (t[k] != t[sz - k - 1]) {
                    ok = false;
                    break;
                }
            }
            if (ok) chmax(ans, sz);
        }
    }
    cout << ans << endl;
}

C - 321-like Searcher

存在する321-like Number の数は、雑に見積もって $ 10! $、使う一桁の整数の集合を決めると並び方は一意に定まるので $ 2 ^ 10 $ となるので全通り作ってソートすれば良い

提出コード

void solve() {
    ll K;
    cin >> K;
    vector<ll> ans;
    queue<ll> q;
    REP(i, 1, 10) q.push(i);
    while (!q.empty()) {
        ll now = q.front();
        q.pop();
        ans.push_back(now);
        int last = now % 10;
        for (int i = last - 1; i >= 0; i--) {
            q.push(now * 10 + i);
        }
    }
    sort(ALL(ans));
    cout << ans[K - 1] << endl;
}

D - Set Menu

$ B $ は昇順に並んでいるものとする
$ A_i $ について、$ P - A_i $ より大きい最小の $ B _ {idx} $ を求める
そうすると、$ idx \times A_i \times + \sum _ {j = 0} \ ^ {idx} B _ j $ と $ P \times (M - idx) $ が答えに加算される
予め $ B $ についてソート、累積和を取っておき上記を二分探索で求めることで解くことができる

提出コード

void solve() {
    ll N, M, P;
    cin >> N >> M >> P;
    vector<ll> A(N), B(M);
    REP(i, N) cin >> A[i];
    REP(i, M) cin >> B[i];
    sort(ALL(B));
    vector<ll> cum(M + 1);
    REP(i, M) cum[i + 1] = cum[i] + B[i];
    ll ans = 0;
    dump(A);
    dump(B);
    REP(i, N) {
        ll idx = upper_bound(ALL(B), P - A[i]) - B.begin();
        ans += idx * A[i] + cum[idx];
        ans += P * (M - idx);
    }
    cout << ans << endl;
}

F - #(subset sum = K) with Add and Erase

クエリで与えられる $ x $ を$ a $ とする
FPSで考えると + の操作は $ (1 + x^a) $ の乗算、- の操作は $ (1 + x^a) $ の除算の操作になる
スパースな場合の乗算・除算を行うと各操作 $ O(K) $ となるので十分実行時間に間に合う

提出コード

void solve() {
    int Q, K;
    cin >> Q >> K;
    const int MAX = 5050;
    FPS<mint> f(MAX);
    f[0] = 1;
    while (Q--) {
        char q;
        int x;
        cin >> q >> x;
        vector<mint> nxt(MAX);
        if (q == '+') {
            if (x <= K) {
                vector<pair<int, mint>> g = {{0, 1}, {x, 1}};
                mul(f, g);
            }
        }
        else {
            if (x <= K) {
                vector<pair<int, mint>> g = {{0, 1}, {x, 1}};
                div(f, g);
            }
        }
        cout << f[K] << endl;
    }
}

AtCoder Beginner Contest 320(ABC320)

atcoder.jp

A - Leyland Number

問題文の通り計算して出力する

提出コード

void solve() {
    ll A, B;
    cin >> A >> B;
    ll a = 1;
    ll b = 1;
    REP(i, B) a *= A;
    REP(i, A) b *= B;
    cout << a + b << endl;
}

B - Longest Palindrome

連続部分列を全通り試し、回分判定を行う

提出コード

void solve() {
    string S;
    cin >> S;
    int N = S.size();
    int ans = 1;
    REP(i, N) {
        REP(j, i + 1, N) {
            string t = "";
            REP(k, i, j + 1) t += S[k];
            int sz = t.size();
            bool ok = true;
            REP(k, sz / 2) {
                if (t[k] != t[sz - k - 1]) {
                    ok = false;
                    break;
                }
            }
            if (ok) chmax(ans, sz);
        }
    }
    cout << ans << endl;
}

C - Slot Strategy 2 (Easy)

$ S_i = S_j $ かつ $ S_j = S_k $ を満たす $ i, j, k $ を考える
$ i, j, k $ が全て異なる場合、$ max(i, j, k) $ となる
それ以外の場合、同じ位置 $ x $ にあるものは $ x + M $ 後に押すことになるのでこれを各場合ごとに求めその最小を出力する

提出コード

void solve() {
    const int N = 3;
    int M;
    cin >> M;
    vector<string> S(N);
    REP(i, N) cin >> S[i];
    int ans = INF;
    REP(i, M) REP(j, M) REP(k, M) {
        if (S[0][i] == S[1][j] and S[0][i] == S[2][k]) {
            int mi = INF;
            if (i == j and i == k) mi = i + M * 2;
            else if (i == j) mi = i + M;
            else if (i == k) mi = i + M;
            else if (j == k) mi = j + M;
            else mi = max({i, j, k});
            chmin(ans, mi);
        }
    }
    cout << (ans == INF ? -1 : ans) << endl;
}

D - Relative Position

人1は原点にいて、与えられる情報は矛盾しないので人1からBFSで到達可能な頂点とその座標を決めていく

提出コード

void solve() {
    ll N, M;
    cin >> N >> M;
    vector<int> A(M), B(M), X(M), Y(M);
    using T = tuple<ll, ll, ll>;
    vector<vector<T>> G(N);
    REP(i, M) {
        cin >> A[i] >> B[i] >> X[i] >> Y[i];
        A[i]--, B[i]--;
        G[A[i]].emplace_back(B[i], X[i], Y[i]);
        G[B[i]].emplace_back(A[i], -X[i], -Y[i]);
    }

    vector<LP> xy(N, {-1, -1});
    vector<bool> used(N, false);
    xy[0] = {0, 0};
    queue<int> q;
    q.emplace(0);
    while (!q.empty()) {
        auto v = q.front();
        q.pop();
        if (used[v]) continue;
        used[v] = true;
        for (auto [nv, x, y] : G[v]) {
            if (used[nv]) continue;
            ll nx = xy[v].first + x;
            ll ny = xy[v].second + y;
            xy[nv] = {nx, ny};
            q.emplace(nv);
        }
    }
    REP(i, N) {
        if (!used[i]) cout << "undecidable" << endl;
        else cout << xy[i].first << " " << xy[i].second << endl;
    }
}

E - Somen Nagashi

イベントソートっぽく各時点におけるそうめんを流すイベントと人 $ i $ が戻ってくるイベントを管理する
人が並んでいる情報をsetなどで持っておき、そうめんが流れてきたときに人がいるかどうかを判定すれば良い

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    // -1、人、null
    // 1、W, S
    using U = tuple<ll, ll, ll>;
    vector<ll> T(M), W(M), S(M);
    Compress<ll> cmp;
    REP(i, M) {
        cin >> T[i] >> W[i] >> S[i];
        cmp.add(T[i]);
        cmp.add(T[i] + S[i]);
    }
    cmp.add(LINF);
    cmp.build();
    int sz = cmp.size();
    vector<vector<U>> event(sz);

    REP(i, M) { event[cmp.get(T[i])].emplace_back(1, W[i], S[i]); }
    set<int> st;
    vector<ll> ans(N);
    REP(i, N) st.emplace(i);
    REP(i, sz) {
        sort(ALL(event[i]));
        for (auto v : event[i]) {
            auto [type, w, s] = v;
            if (type == -1) {
                st.emplace(w);
            }
            else {
                if (!st.empty()) {
                    auto p = *st.begin();
                    ans[p] += w;
                    ll nxt_t = cmp[i] + s;
                    event[cmp.get(nxt_t)].emplace_back(-1, p, 0);
                    st.erase(st.begin());
                }
            }
        }
    }
    REP(i, N) cout << ans[i] << endl;
}

AtCoder Beginner Contest 312(ABC312)

atcoder.jp

A - Chord

問題文のとおりに判定する

提出コード

void solve() {
    string S;
    cin >> S;
    vector<string> T = {"ACE", "BDF", "CEG", "DFA", "EGB", "FAC", "GBD"};
    for (auto t : T) {
        if (S == t) {
            cout << "Yes" << endl;
            return;
        }
    }
    cout << "No" << endl;
}

B - TaK Code

サンプルにTak Codeがあるのでそれを利用する
左上の点の位置を全探索し、#. の点の位置全て一致するかを判定する

###.?????
###.?????
###.?????
....?????
?????????
?????....
?????.###
?????.###
?????.###

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    vector<string> S(N);
    REP(i, N) cin >> S[i];
    vector<string> task = {
        "###.?????",
        "###.?????",
        "###.?????",
        "....?????",
        "?????????",
        "?????....",
        "?????.###",
        "?????.###",
        "?????.###",
    };
    REP(i, N - 9 + 1) REP(j, M - 9 + 1) {
        int cnt = 0;
        REP(h, 9) REP(w, 9) {
            if (task[h][w] == '?') cnt++;
            else if (task[h][w] == S[i + h][j + w]) cnt++;
        }
        if (cnt == 9 * 9) {
            cout << i + 1 << " " << j + 1 << endl;
        }
    }
}

C - Invisible Hand

$ X $ 円のときに条件を満たすかどうかを判定できるのでこれを二分探索で求める

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    vector<int> A(N), B(M);
    REP(i, N) cin >> A[i];
    REP(i, M) cin >> B[i];
    sort(ALL(A));
    sort(ALL(B));
    ll l = 0, r = 2 * INF;
    while (r - l > 1) {
        ll m = (l + r) >> 1;
        ll cnt_a = 0;
        ll cnt_b = 0;
        REP(i, N) if (A[i] <= m) cnt_a++;
        REP(i, M) if (B[i] >= m) cnt_b++;
        if (cnt_a >= cnt_b) r = m;
        else l = m;
    }
    cout << r << endl;
}

D - Count Bracket Sequences

括弧列は ( のとき +1 、 ) のとき -1 としたときの累積和が途中で負にならず、合計が0となるものであるので
$ dp[i][j] := i $ 番目の文字まで見たときの累積和が $ j $ であるときの場合の数としてDPを行えば良い

提出コード

void solve() {
    string S;
    cin >> S;
    const int N = S.size();
    vector<mint> dp(N + 110);
    dp[0] = 1;
    REP(i, N) {
        vector<mint> nxt(N + 10);
        REP(j, N + 1) {
            if (S[i] == '(') {
                nxt[j + 1] += dp[j];
            }
            else if (S[i] == ')') {
                if (j > 0) nxt[j - 1] += dp[j];
            }
            else {
                nxt[j + 1] += dp[j];
                if (j > 0) nxt[j - 1] += dp[j];
            }
        }
        swap(dp, nxt);
    }
    cout << dp[0] << endl;
}

E - Tangency of Cuboids

それぞれの立方体が重なることはないので $ 1 \times 1 \times 1 $ の立方体は高々1つの立方体に属することになる
また、各立方体は上下左右前後の6方向に対して別の立方体が属する $ 1 \times 1 \times 1 $ の立方体が存在すれば面で接することになる
立方体の数は制約から最大で $ 10 ^ 6 $ 程度であるので全探索をすれば良い

提出コード

void solve() {
    int N;
    cin >> N;
    vector<int> X1(N), Y1(N), Z1(N), X2(N), Y2(N), Z2(N);
    vector cube(111, vector(111, vector(111, -1)));
    REP(i, N) {
        cin >> X1[i] >> Y1[i] >> Z1[i] >> X2[i] >> Y2[i] >> Z2[i];
        REP(x, X1[i], X2[i]) REP(y, Y1[i], Y2[i]) REP(z, Z1[i], Z2[i]) cube[x][y][z] = i;
    }
    vector<set<int>> st(N);
    REP(x, 101) REP(y, 101) REP(z, 101) if (cube[x][y][z] != -1) {
        if (cube[x + 1][y][z] != -1 and cube[x + 1][y][z] != cube[x][y][z]) {
            st[cube[x + 1][y][z]].emplace(cube[x][y][z]);
            st[cube[x][y][z]].emplace(cube[x + 1][y][z]);
        }
        if (cube[x][y + 1][z] != -1 and cube[x][y + 1][z] != cube[x][y][z]) {
            st[cube[x][y + 1][z]].emplace(cube[x][y][z]);
            st[cube[x][y][z]].emplace(cube[x][y + 1][z]);
        }
        if (cube[x][y][z + 1] != -1 and cube[x][y][z + 1] != cube[x][y][z]) {
            st[cube[x][y][z + 1]].emplace(cube[x][y][z]);
            st[cube[x][y][z]].emplace(cube[x][y][z + 1]);
        }
    }
    REP(i, N) cout << st[i].size() << endl;
}

F - Cans and Openers

$ T_i = 2 $ のものを使うときは $ X_i $ が大きいものから順に使っていけばいい
また、$ T_i = 0 $ のものははじめから全て集合に追加されているとし、$ T_i = 2 $ のものを選んだときの $ X_i $ の合計だけ $ T_i = 1 $ の要素を降順に集合に追加していくと考える
そうすると、今ある集合から降順 $ k $ 個の総和を求める事ができれば良い
これはBITやpriority_queueを用いれば高速に求めることができるので、$ T_i = 2 $ のものを $ i $ 個選び、$ X_i $ 個だけ $ T_i = 1 $ のものが追加された集合の降順 $ M - i $ 個の総和をそれぞれ求め、その最大を答えれば良い

提出コード

void solve() {
    int N, M;
    cin >> N >> M;
    vector<int> T(N), X(N);
    vector<vector<int>> TX(3);
    MaximumSum<ll> sum(M);
    REP(i, N) {
        cin >> T[i] >> X[i];
        TX[T[i]].push_back(X[i]);
    }
    REP(i, M) sum.insert(0);
    for (auto x : TX[0]) {
        sum.insert(x);
    }
    priority_queue<int> pq;
    for (auto x : TX[1]) {
        pq.push(x);
    }
    sort(ALL(TX[2]));
    reverse(ALL(TX[2]));
    ll ans = sum.query();
    int used = 0;
    for (auto x : TX[2]) {
        while (x > 0 && !pq.empty()) {
            int y = pq.top();
            pq.pop();
            sum.insert(y);
            x--;
            dump(y);
        }
        used++;
        if (M - used <= 0) break;
        sum.set_k(M - used);
        chmax(ans, sum.query());
    }
    cout << ans << endl;
}