PS

BOJ 17410 : 수열과 쿼리 1.5

lickelon 2024. 12. 3. 23:51
  • 문제 링크 : boj.kr/17410
  • 난이도 : D5
  • 태그 : 제곱근 분할법, 누적합

코드

#include <bits/stdc++.h>

#define all(x) (x).begin(), (x).end()

#define INF 0x7FFFFFFF

using namespace std;

using ll = long long;
using ld = long double;
using pii = pair<int,int>;
using pll = pair<ll, ll>;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int n;
    cin >> n;
    vector<int> arr(n);
    for(auto &e : arr) cin >> e;
    int s = sqrt(n);
    vector<array<int, 10001>> bucket(n/s+1);
    for(int i = 0; i < n; i++) {
        bucket[i/s][arr[i]]++;
    }
    for(int i = 1; i < bucket.size(); i++) {
        for(int j = 0; j <= 10000; j++) {
            bucket[i][j] += bucket[i-1][j];
        }
    }

    int m;
    cin >> m;
    for(int i = 0; i < m; i++) {
        int q;
        cin >> q;
        if(q == 1) {
            int idx, v;
            cin >> idx >> v;
            idx--;
            for(int i = idx / s; i < bucket.size(); i++) {
                bucket[i][arr[idx]]--;
                bucket[i][v]++;
            }
            arr[idx] = v;
        }
        if(q == 2) {
            int i, j, k;
            cin >> i >> j >> k;
            i--, j--;
            int ix = i/s, jx = j/s;
            int ans = 0;
            for(int t = k+1; t <= 10000; t++) {
                int r = bucket[jx][t];
                int l = (ix > 0) ? bucket[ix-1][t] : 0;
                ans += r-l;
            }
            for(int t = ix*s; t < i; t++) {
                ans -= (arr[t] > k);
            }
            for(int t = j+1; t < min(jx*s+s, n); t++) {
                ans -= (arr[t] > k);
            }
            cout << ans << "\n";
        }
    }


    return 0;
}

풀이

#14504와 비슷한 문제이지만, 시간제한이 더 작고, 수의 범위도 작다.

수의 범위가 작기 때문에 각 버킷에 있는 수의 개수를 세어 저장할 수 있다.

이제 남은 것은 한 버킷부터 다른 버킷 사이에 있는 어떤 수를 $O(1)$로 세는 것이다.

누적합을 이용해주면 $O(1)$로 셀 수 있고, 따라서 2번 쿼리의 경우 $O(\sqrt N+10000)$으로 처리할 수 있다.

1번 쿼리의 경우 누적합을 업데이트 하는 것이므로 $O(\sqrt N)$으로 처리할 수 있다.

728x90