接着剤の精進日記

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

AtCoder Beginner Contest 264(ABC264)

atcoder.jp

A - "atcoder".substr()

$ S_L $ から $ S_R $ までを出力

提出コード

void solve(){
    int L, R;
    cin >> L >> R;
    string S = "atcoder";
    for (int i = L - 1; i <= R - 1;i++){
        cout << S[i];
    }
    cout << endl;
}

B - Nice Grid

if文で全通り書く

提出コード

void solve() {
    int R, C;
    cin >> R >> C;
    string ans = "white";
    if (R == 1 or R == 15) {
        ans = "black";
    }
    else if (R == 2 or R == 14) {
        if (C == 1 or C == 15) ans = "black";
    }
    else if (R == 3 or R == 13) {
        if (C == 1 or C == 15) ans = "black";
        if (3 <= C and C <= 13) ans = "black";
    }
    else if (R == 4 or R == 12) {
        if (C == 1 or C == 3 or C == 13 or C == 15) ans = "black";
    }
    else if (R == 5 or R == 11) {
        if (C == 1 or C == 3 or C == 13 or C == 15) ans = "black";
        if (5 <= C and C <= 11) ans = "black";
    }
    else if (R == 6 or R == 10) {
        if (C == 1 or C == 3 or C == 5 or C == 11 or C == 13 or C == 15) ans = "black";
    }
    else if (R == 7 or R == 9) {
        if (C == 1 or C == 3 or C == 5 or C == 11 or C == 13 or C == 15) ans = "black";
        if (7 <= C and C <= 9) ans = "black";
    }
    else if (R == 8) {
        if (C == 1 or C == 3 or C == 5 or C == 7 or C == 9 or C == 11 or C == 13 or C == 15) ans = "black";
    }
    cout << ans << endl;
}

C - Matrix Reducing

どの列、行を残すかをそれぞれbit全探索を行い、一致させることができるかを判定

提出コード

void solve() {
    int H1, W1, H2, W2;
    cin >> H1 >> W1;
    vector A(H1, vector(W1, 0));
    REP(i, H1) REP(j, W1) cin >> A[i][j];
    cin >> H2 >> W2;
    vector B(H2, vector(W2, 0));
    REP(i, H2) REP(j, W2) cin >> B[i][j];
    bool ok = false;
    REP(i, 1 << H1) {
        if (__builtin_popcount(i) != H2) continue;
        REP(j, 1 << W1) {
            if (__builtin_popcount(j) != W2) continue;
            vector<vector<int>> C(H2, vector<int>(W2, 0));
            int ii = 0;
            REP(k, H1) if (i >> k & 1) {
                int jj = 0;
                REP(l, W1)
                if (j >> l & 1) {
                    C[ii][jj] = A[k][l];
                    jj++;
                }
                ii++;
            }
            if (C == B) ok = true;
        }
    }
    cout << (ok ? "Yes" : "No") << endl;
}

D - "redocta".swap(i,i+1)

隣接スワップの最小操作回数は転倒数になるため、文字列を数列とみなしてその転倒数を求める

提出コード

ll inversionNumber(string &x, string &y){
    // xをyにするために必要な隣接swapの回数(転倒数)を返す
    // 同じ値が含まれる場合、最小回数を返す
    int sz = x.size();

    assert(sz == (int)y.size());

    BIT<ll> bit(sz);

    // 座圧
    map<char, vector<int>> idx;
    for(int i=sz-1;i>=0;i--){
        idx[x[i]].emplace_back(i);
    }
    ll res = 0;
    for(int i=0;i<sz;i++){
        vector<int> &v = idx[y[i]];
        // xをyに出来ない時 -1 を返す
        if(v.empty()) return -1;

        int id = v.back(); v.pop_back();
        res += id - bit.sum(id);
        bit.add(id+1, 1);
    }
    return res;
}

void solve(){
    string S;
    cin >> S;
    string T = "atcoder";
    cout << inversionNumber(S, T) << endl;
}

E - Blackout 2

UnionFindを使って、クエリの逆順に答えを求めていく
都市 $ x $ が発電所とつながっているとき、$ contain[x] = true $ とし、$ (x, y) $ を uniteするとき、発電所と繋がっていない方の都市の数を答えに加算していく

提出コード

struct UnionFind {
    vector<int> par;
    int _N;
    vector<int> sum;
    vector<bool> contain;
    ll ans;
    UnionFind(int n, int _N) : par(n, -1), sum(n, 0), contain(n, false), ans(0)
    {
        REP(i, _N){
            sum[i] = 1;
        }
        REP(i, _N, n){
            contain[i] = true;
        }
    }
    void init(int n) { par.assign(n, -1); }

    int root(int x) {
        if (par[x] < 0) return x;
        else return par[x] = root(par[x]);
    }

    bool issame(int x, int y) {
        return root(x) == root(y);
    }

    bool unite(int x, int y) {
        x = root(x); y = root(y);
        if (x == y) return false;
        if (par[x] > par[y]) swap(x, y); // merge technique
        if(contain[x] and contain[y]){}
        else if(contain[x]){
            contain[y] = true;
            ans += sum[y];
        }
        else if(contain[y]){
            contain[x] = true;
            ans += sum[x];
        }
        sum[x] += sum[y];
        par[x] += par[y];
        par[y] = x;
        return true;
    }

    int size(int x) {
        return -par[root(x)];
    }
};

void solve() {
    int N, M, E;
    cin >> N >> M >> E;
    vector<pair<int, int>> edge(E);
    REP(i, E) {
        int u, v;
        cin >> u >> v;
        edge[i] = {--u, --v};
    }
    int Q;
    cin >> Q;
    vector<int> x(Q);
    vector<int> used(E);
    REP(i, Q) {
        cin >> x[i];
        --x[i];
        used[x[i]] = 1;
    }
    UnionFind uf(N + M, N);
    REP(i, E) if (!used[i]) { uf.unite(edge[i].first, edge[i].second); }
    vector<int> ans(Q);
    for (int i = Q - 1; i >= 0; i--) {
        ans[i] = uf.ans;
        uf.unite(edge[x[i]].first, edge[x[i]].second);
    }
    REP(i, Q) cout << ans[i] << endl;
}

F - Monochromatic Path

マス $ (i, j) $ にいるときに行 $ i $ 、列 $ j $ より手前の状態については考慮しなくていいので
$ dp[i][j][row][col] := i $ 行、$ j $ 列にいるときに、$ i $ 行目が反転した状態かどうか($ row $)、$ j $ 列目が反転した状態かどうか($ col $)の場合の最小値としてDPを行う

提出コード

void solve() {
    int H, W;
    cin >> H >> W;
    vector<ll> R(H), C(W);
    REP(i, H) { cin >> R[i]; }
    REP(i, W) { cin >> C[i]; }
    vector<string> A(H);
    REP(i, H) { cin >> A[i]; }
    ll ans = LINF;
    REP(_, 2) {
        vector dist(2, vector(2, vector(H, vector(W, LINF))));

        if (A[0][0] == '0') {
            dist[0][0][0][0] = 0;
            dist[1][1][0][0] = R[0] + C[0];
        }
        else {
            dist[1][0][0][0] = R[0];
            dist[0][1][0][0] = C[0];
        }
        REP(h, H) REP(w, W) REP(row, 2) REP(col, 2) {
            // 下
            {
                int nh = h + 1;
                int nw = w;
                if (nh < 0 or nh >= H or nw < 0 or nw >= W) {
                }
                else {
                    char c = A[nh][nw];
                    if (col) {
                        if (c == '0') c = '1';
                        else c = '0';
                    }
                    if (c == '0') {
                        chmin(dist[0][col][nh][nw], dist[row][col][h][w]);
                    }
                    else {
                        // nh 行を反転
                        chmin(dist[1][col][nh][nw], dist[row][col][h][w] + R[nh]);
                    }
                }
            }
            // 右
            {
                int nh = h;
                int nw = w + 1;
                if (nh < 0 or nh >= H or nw < 0 or nw >= W) {
                }
                else {
                    char c = A[nh][nw];
                    if (row) {
                        if (c == '0') c = '1';
                        else c = '0';
                    }
                    if (c == '0') {
                        chmin(dist[row][0][nh][nw], dist[row][col][h][w]);
                    }
                    else {
                        // nw 列を反転
                        chmin(dist[row][1][nh][nw], dist[row][col][h][w] + C[nw]);
                    }
                }
            }
        }
        chmin(ans,
              min({dist[0][0][H - 1][W - 1],
                   dist[1][0][H - 1][W - 1],
                   dist[0][1][H - 1][W - 1],
                   dist[1][1][H - 1][W - 1]}));
        REP(i, H) REP(j, W) {
            if (A[i][j] == '0') A[i][j] = '1';
            else A[i][j] = '0';
        }
    }

    cout << ans << endl;
}