接着剤の精進日記

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

AtCoder Beginner Contest 375(ABC375)

atcoder.jp

A - Seats

#.# の数を数える

提出コード

void solve() {
    int N;
    string S;
    cin >> N >> S;
    int ans = 0;
    REP(i, 1, N - 1) {
        if (S[i - 1] == '#' and S[i] == '.' and S[i + 1] == '#') ans++;
    }
    cout << ans << endl;
}

B - Traveling Takahashi Problem

距離を順に足していく

提出コード

void solve() {
    int N;
    cin >> N;
    ll x = 0, y = 0;
    double ans = 0;
    REP(N) {
        ll a, b;
        cin >> a >> b;
        ans += hypot(x - a, y - b);
        x = a, y = b;
    }
    ans += hypot(x, y);
    cout << fixed << setprecision(10) << ans << endl;
}

C - Spiral Rotation

各点ごとに何回操作されるかが決まり、各操作は $ \pmod{4} $ でどこに移動するか求められるので各点ごとにどこに移動するかが定まる

提出コード

void solve() {
    int N;
    cin >> N;
    vector<string> A(N);
    REP(i, N) cin >> A[i];
    vector B = A;
    REP(x, N) {
        REP(y, N) {
            int cnt = (min({x, N - x - 1, y, N - y - 1}) + 1) % 4;
            int nx = x, ny = y;
            REP(k, cnt) {
                int tmp = nx;
                nx = ny;
                ny = N - 1 - tmp;
            }
            dump(x, y, nx, ny, cnt);
            B[nx][ny] = A[x][y];
        }
    }
    REP(i, N) cout << B[i] << endl;
}

D - ABA

中心を固定して考える
このとき、回文になる個数は中心から左右にある同じ文字の選び方の総和になる
そのため、中心から左右の文字ごとの個数を管理し、各文字ごとに選び方の総和を加算していく

提出コード

void solve() {
    string S;
    cin >> S;
    vector<ll> l_cnt(26, 0), r_cnt(26, 0);
    REP(S.size()) r_cnt[S[i] - 'A']++;
    ll ans = 0;
    int N = S.size();
    r_cnt[S[0] - 'A']--;
    l_cnt[S[0] - 'A']++;
    REP(i, 1, N - 1) {
        r_cnt[S[i] - 'A']--;
        ll cnt = 0;
        REP(j, 26) {
            if (l_cnt[j] > 0 && r_cnt[j] > 0) ans += l_cnt[j] * r_cnt[j];
        }
        l_cnt[S[i] - 'A']++;
    }
    cout << ans << endl;
}

E - 3 Team Division

$ dp[i][j][k] := i $ 番目まで見たときに、チーム2の総和が $ j $ 、チーム3の総和が $ k $ となるように必要な操作回数の最小としてDPをする

提出コード

void solve() {
    int N;
    cin >> N;
    vector<int> A(N), B(N);
    REP(i, N) { cin >> A[i] >> B[i]; }
    int sum = accumulate(ALL(B), 0);
    if (sum % 3) {
        cout << -1 << endl;
        return;
    }
    vector dp(501, vector<int>(501, INF));
    dp[0][0] = 0;
    REP(i, N) {
        vector ndp(501, vector<int>(501, INF));
        REP(j, 501) {
            REP(k, 501) {
                if (dp[j][k] == INF) continue;
                // チーム1から移動
                if (A[i] == 1) {
                    chmin(ndp[j][k], dp[j][k]);
                    if (j + B[i] < 501) chmin(ndp[j + B[i]][k], dp[j][k] + 1);
                    if (k + B[i] < 501) chmin(ndp[j][k + B[i]], dp[j][k] + 1);
                }
                // チーム2から移動
                if (A[i] == 2) {
                    chmin(ndp[j][k], dp[j][k] + 1);
                    if (j + B[i] < 501) chmin(ndp[j + B[i]][k], dp[j][k]);
                    if (k + B[i] < 501) chmin(ndp[j][k + B[i]], dp[j][k] + 1);
                }
                // チーム3から移動
                if (A[i] == 3) {
                    chmin(ndp[j][k], dp[j][k] + 1);
                    if (j + B[i] < 501) chmin(ndp[j + B[i]][k], dp[j][k] + 1);
                    if (k + B[i] < 501) chmin(ndp[j][k + B[i]], dp[j][k]);
                }
            }
        }
        swap(dp, ndp);
    }
    cout << (dp[sum / 3][sum / 3] == INF ? -1 : dp[sum / 3][sum / 3]) << endl;
}

F - Road Blocked

クエリを逆から見て、辺の削除を辺の追加クエリとして考える
あらかじめワーシャルフロイドで全点間の最短距離を求めておく
このとき、辺を追加したときの更新クエリは $ O(N^2) $ で更新でき、また更新クエリが $ 300 $ 以下であるので全体で $ O(N^3) $ で更新クエリを行うことができる

提出コード

void solve() {
    int N, M, Q;
    cin >> N >> M >> Q;
    vector<ll> A(M), B(M), C(M);
    REP(i, M) {
        cin >> A[i] >> B[i] >> C[i];
        A[i]--, B[i]--;
    }
    using T = tuple<int, int, int>;
    vector<T> query(Q);
    set<int> removed_edge;
    REP(i, Q) {
        int x = -1, y = -1, z = -1;
        cin >> x;
        if (x == 1) cin >> y;
        else cin >> y >> z;
        query[i] = T(x, y - 1, z - 1);
        if (x == 1) removed_edge.insert(y - 1);
    }
    vector<vector<ll>> G(N, vector<ll>(N, LINF));
    REP(i, M) {
        if (removed_edge.contains(i)) continue;
        G[A[i]][B[i]] = C[i];
        G[B[i]][A[i]] = C[i];
    }
    REP(i, N) { G[i][i] = 0; }
    REP(k, N) REP(i, N) REP(j, N) {
        if (G[i][k] == LINF or G[k][j] == LINF) continue;
        chmin(G[i][j], G[i][k] + G[k][j]);
    }
    vector<ll> ans;
    reverse(ALL(query));
    for (auto [x, y, z] : query) {
        if (x == 1) {
            ll X = A[y], Y = B[y], Z = C[y];
            chmin(G[X][Y], Z);
            chmin(G[Y][X], Z);
            REP(i, N) {
                REP(j, N) {
                    if (i != j) {
                        chmin(G[i][j], G[i][X] + G[Y][j] + Z);
                        chmin(G[i][j], G[i][Y] + G[X][j] + Z);
                    }
                }
            }
        }
        else {
            if (G[y][z] == LINF) ans.push_back(-1);
            else ans.push_back(G[y][z]);
        }
    }
    reverse(ALL(ans));
    for (auto a : ans)
        cout << a << endl;
}