11/22 Subsequence

AtCoder Beginner Contest 381 E

1 min readPSAtCoder

문제

풀이

어떤 /의 위치 ii에 대해 이보다 왼쪽에 있는 11의 개수를 oo, 오른쪽에 있는 22의 개수를 tt라 합시다.

그렇다면 ii를 중심으로 하는 부분 11/22 문자열의 최대 길이는 2min⁡(o,t)+12\min(o, t) + 1이 됩니다.

이때 ii가 증가하면 oo는 증가하고 tt는 감소하므로 min⁡(o,t)\min(o, t)는 ii가 증가함에 따라 증가하다가 감소하는 형태를 띕니다.

따라서 o≤to \leq t를 만족하는 가장 오른쪽에 있는 /, 혹은 o≥to \geq t를 만족하는 가장 왼쪽에 있는 /만 검사하면 최댓값을 찾을 수 있습니다.

이때 조건 o≤to \leq t는 단조성을 띄므로 이분 탐색을 통해 해당 /의 위치를 찾을 수 있습니다.

누적 합을 미리 전처리한 다음 이분 탐색을 이용해 후보를 찾으면 쿼리마다 O(log⁡N)O(\log N)에 답할 수 있습니다.

따라서 O(N+Qlog⁡N)O(N + Q\log N)에 문제를 해결할 수 있습니다.

코드

#include <bits/stdc++.h>
using namespace std;
 
#define all(v) v.begin(), v.end()
 
int cnt[101010][2];
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int N, Q;
    cin >> N >> Q;
 
    string S;
    cin >> S;
    S = "#" + S;
 
    vector<int> slashes;
 
    for (int i = 1; i <= N; i++) {
        cnt[i][0] = cnt[i - 1][0];
        cnt[i][1] = cnt[i - 1][1];
 
        if (S[i] == '/') {
            slashes.push_back(i);
            continue;
        }
 
        int num = S[i] - '1';
        cnt[i][num] += 1;
    }
 
    for (int q = 0; q < Q; q++) {
        int L, R;
        cin >> L >> R;
 
        int l = lower_bound(all(slashes), L) - slashes.begin();
        int r = upper_bound(all(slashes), R) - slashes.begin() - 1;
 
        if (r < l) {
            cout << 0 << '\n';
            continue;
        }
 
        int lo = l, hi = r;
        while (lo < hi) {
            int m = (lo + hi) / 2;
 
            int i = slashes[m];
 
            if (cnt[i][0] - cnt[L - 1][0] < cnt[R][1] - cnt[i][1]) lo = m + 1;
            else hi = m;
        }
 
        int i = slashes[lo];
        int half = min(cnt[i][0] - cnt[L - 1][0], cnt[R][1] - cnt[i][1]);
 
        if (l <= lo - 1) {
            i = slashes[lo - 1];
            half = max(half, min(cnt[i][0] - cnt[L - 1][0], cnt[R][1] - cnt[i][1]));
        }
 
        cout << 2*half + 1 << '\n';
    }
 
    return 0;
}

댓글

이름과 이메일을 입력해 댓글을 남겨주세요.