Codeforces Round 903 (Div. 3) 풀이

이번 라운드의 A~G번 문제에 대한 핵심 아이디어와 구현 방법을 정리합니다.

A. Don't Try to Count

문자열 ts의 연속 부분문자열이 되도록 만드는 문제입니다. s를 반복하여 이어붙이면 길이가 2배로 늘어나는 특성을 활용합니다. n·m ≤ 25 조건 덕분에 최대 5번만 반복하면 충분합니다.

#include <bits/stdc++.h>
using namespace std;

bool isSubstr(const string& text, const string& pat) {
    if (pat.empty()) return true;
    for (size_t i = 0; i + pat.length() <= text.length(); ++i) {
        if (text.substr(i, pat.length()) == pat) return true;
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        int n, m;
        string base, target;
        cin >> n >> m >> base >> target;
        
        int res = -1;
        for (int step = 0; step <= 5; ++step) {
            if (isSubstr(base, target)) {
                res = step;
                break;
            }
            base += base;
        }
        cout << res << "\n";
    }
    return 0;
}

B. Three Threadlets

세 개의 실을 잘라 길이가 같은 조각들로 만드는 문제입니다. 잘라야 하는 횟수가 6번 이하여야 하므로, 가능한 조각 개수(3, 4, 5, 6) 각각에 대해 검증합니다.

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

bool valid(ll a, ll b, ll c, ll pieces) {
    ll total = a + b + c;
    if (total % pieces != 0) return false;
    ll unit = total / pieces;
    if (a % unit || b % unit || c % unit) return false;
    ll cuts = a/unit + b/unit + c/unit - 3;
    return cuts <= 3;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        ll x, y, z;
        cin >> x >> y >> z;
        bool ok = valid(x,y,z,3) || valid(x,y,z,4) || valid(x,y,z,5) || valid(x,y,z,6);
        cout << (ok ? "YES" : "NO") << "\n";
    }
    return 0;
}

C. Perfect Square

행렬을 회전시켜 완전 제곱수 형태로 만드는 문제입니다. 중심을 기준으로 4개 대칭 위치를 묶어 각 위치의 최대값으로 통일시키는 비용을 계산합니다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        int n;
        cin >> n;
        vector<string> g(n);
        for (auto& row : g) cin >> row;
        
        int cost = 0;
        int half = n / 2;
        for (int i = 0; i < half; ++i) {
            for (int j = 0; j < half; ++j) {
                int a = g[i][j] - '0';
                int b = g[j][n-1-i] - '0';
                int c = g[n-1-j][i] - '0';
                int d = g[n-1-i][n-1-j] - '0';
                int mx = max({a, b, c, d});
                cost += (mx - a) + (mx - b) + (mx - c) + (mx - d);
            }
        }
        cout << cost << "\n";
    }
    return 0;
}

D. Divide and Equalize

연산 과정에서 전체 곱이 불변임을 이용합니다. 각 수를 소인수분해하여 모든 소인수의 총 개수가 n으로 나누어떨어지는지 확인합니다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        int n;
        cin >> n;
        unordered_map<int, int> freq;
        
        for (int i = 0; i < n; ++i) {
            long long v;
            cin >> v;
            for (long long p = 2; p * p <= v; ++p) {
                while (v % p == 0) {
                    freq[p]++;
                    v /= p;
                }
            }
            if (v > 1) freq[v]++;
        }
        
        bool ok = true;
        for (auto& [p, c] : freq) {
            if (c % n != 0) { ok = false; break; }
        }
        cout << (ok ? "YES" : "NO") << "\n";
    }
    return 0;
}

E. Block Sequence

위치 i에서 i + a[i]로 점프하는 구조를 역순으로 동적 계획법으로 처리합니다. dp[i]i번째부터 끝까지 고려했을 때의 최소 삭제 횟수로 정의합니다.

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        int n;
        cin >> n;
        vector<int> a(n + 2), dp(n + 3, 0);
        for (int i = 1; i <= n; ++i) cin >> a[i];
        
        dp[n + 1] = 0;
        for (int i = n; i >= 1; --i) {
            int nxt = i + a[i] + 1;
            int keep = (nxt <= n + 1) ? dp[nxt] : n + 1;
            dp[i] = min(dp[i + 1] + 1, keep);
        }
        cout << dp[1] << "\n";
    }
    return 0;
}

F. Minimum Maximum Distance

트리 위에서 각 정점까지의 최대 거리를 최소화하는 문제입니다. 실제로는 임의의 표记된 정점에서 BFS를 두 번 수행하여 지름을 구하면 해결됩니다.

지름 길이를 d라 할 때, 답은 ⌈d / 2⌉입니다.

G. Anya and the Mysterious String

구간 내에 회문이 존재하는지 판별하는 문제입니다. 길이 2 또는 3인 회문만 검사하면 충분하며, 펜윅 트리로 구간 회전을 처리하고 set으로 회문 위치를 관리합니다.

#include <bits/stdc++.h>
using namespace std;

struct Fenwick {
    int n;
    vector<int> bit;
    Fenwick(int n = 0) { init(n); }
    void init(int n_) {
        n = n_;
        bit.assign(n + 2, 0);
    }
    void add(int idx, int val) {
        for (; idx <= n; idx += idx & -idx) bit[idx] = (bit[idx] + val) % 26;
    }
    int sum(int idx) {
        int res = 0;
        for (; idx > 0; idx -= idx & -idx) res = (res + bit[idx]) % 26;
        return (res % 26 + 26) % 26;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int tc;
    cin >> tc;
    while (tc--) {
        int n, q;
        cin >> n >> q;
        string str;
        cin >> str;
        
        Fenwick fw(n);
        for (int i = 1; i <= n; ++i) {
            fw.add(i, (str[i-1] - 'a') - fw.sum(i-1));
        }
        
        set<int> len2, len3;
        auto refresh = [&](int pos) {
            if (pos < 1 || pos > n) return;
            if (pos < n) {
                if (fw.sum(pos) == fw.sum(pos + 1)) len2.insert(pos);
                else len2.erase(pos);
            }
            if (pos < n - 1) {
                if (fw.sum(pos) == fw.sum(pos + 2)) len3.insert(pos);
                else len3.erase(pos);
            }
        };
        
        for (int i = 1; i < n; ++i) refresh(i);
        
        while (q--) {
            int type;
            cin >> type;
            if (type == 1) {
                int l, r, k;
                cin >> l >> r >> k;
                k = (k % 26 + 26) % 26;
                fw.add(l, k); fw.add(r + 1, -k);
                for (int d = -2; d <= 2; ++d) refresh(l + d);
                for (int d = -2; d <= 2; ++d) refresh(r + d);
            } else {
                int l, r;
                cin >> l >> r;
                bool bad = false;
                auto it2 = len2.lower_bound(l);
                if (it2 != len2.end() && *it2 <= r - 1) bad = true;
                auto it3 = len3.lower_bound(l);
                if (it3 != len3.end() && *it3 <= r - 2) bad = true;
                cout << (bad ? "NO" : "YES") << "\n";
            }
        }
    }
    return 0;
}

태그: Codeforces competitive-programming dynamic-programming tree-diameter fenwick-tree

7월 31일 23:32에 게시됨