문제 풀이/Baekjoon Online Judge

[Baekjoon - 3411] Jewel heist

hibye1217 2023. 3. 14. 17:39

난이도: Platinum I

 

태그

더보기
  • Sweeping
  • Segment Tree
  • TreeSet
  • Coordinate Compression

 

풀이

1. 모든 색깔을 가지지 않는 확실한 방법은?

더보기

어떤 하나의 색깔을 고의적으로 피한다면, 적어도 그 색깔을 가지지 않을 것임은 분명하니

모든 색깔을 가지지 않을 수 있습니다.

 

2. 특정 색깔을 가지지 않겠다고 해보자. 그럼, 가져갈 수 있는 개수의 최댓값은?

더보기

그 색깔을 가지고 있는 점의 위치를 \( (y_1, x_1), (y_2, x_2), \ldots, (y_N, x_N) \)이라고 해봅시다.

또한, \( y_1 \le y_2 \le \ldots y_N \)이라고 해봅시다.

 

이제 저희가 설치할 Robotic Hand의 \( y \)를 천천히 늘려나가봅시다.

충분히 작은 \( y \)값에 대해서는, 고려해야 할 점이 없으므로 구간 전체에 설치할 수 있습니다.

// 엄밀히는 \( y < y_1 \)인 동안을 의미합니다.

 

그러다가, \( y = y_1 \)이 되었다고 해봅시다.

그 때부터는 \( x_1 \)을 포함시킨 구간으로는 설치할 수 없기 때문에, \( (-\infty, x_1) \)로 설치하는 경우와 \( (x_1, \infty) \)로 설치하는 경우 2가지로 나뉘게 됩니다.


이 이후로도 \( y \)를 올려가면서 새로운 점을 만날 때마다, 하나의 구간이 2개로 나뉘게 되겠죠.

 

이렇게 하면 경우가 너무 많아지지 않을까 걱정하게 될 수도 있습니다.

하지만, 하나의 점은 최대 2개의 구간만을 새로 만들어내므로, 총 구간 개수는 \( O(N) \)이 됩니다.

 

3. [2]를 모든 색깔에 대해 돌리는 건 느릴텐데, 더 빠르게 할 수 있을까?

더보기

색깔을 하나씩 돌릴 필요는 없습니다.

\( y \)를 늘려나가면서 [2]를 적용하는 과정을 모든 색깔에 대해 병렬적으로 처리할 수 있기 때문이죠.

 

모든 색깔에 대해 병렬적으로 처리를 하게 된다면, 스위핑을 1번만 해주면 되니 충분히 빠른 시간에 답을 구할 수 있게 됩니다.

 

4. 코드

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const int N = 262144;
int seg[524290];
void upd(int pos, int val){ pos += N-1;
    seg[pos] += val; pos >>= 1;
    while (pos){ seg[pos] = seg[pos<<1+ seg[pos<<1|1]; pos >>= 1; }
}
int qry(int st, int ed){ st += N-1; ed += N-1;
    int res = 0;
    while (st <= ed){
        if (st&1){ res += seg[st]; st++; }
        if (~ed&1){ res += seg[ed]; ed--; }
        if (st > ed){ break; } st >>= 1; ed >>= 1;
    }
    return res;
}
 
pi3 arr[200020];
vector<int> cs;
inline int cvt(int x){ return lower_bound(all(cs), x)-cs.begin() + 1; }
 
set<pi2> qrr[200020];
 
void Main(){
    int t; cin >> t; while (t--){
        int n, m; cin >> n >> m;
        for (int i = 1; i <= n; i++){
            cin >> arr[i].fr.sc >> arr[i].fr.fr >> arr[i].sc;
            cs.push_back(arr[i].fr.sc);
        }
        unq(cs); for (int i = 1; i <= n; i++){ arr[i].fr.sc = cvt(arr[i].fr.sc); }
        for (int i = 1; i <= m; i++){ qrr[i].insert({1, cs.size()}); }
        sort(arr+1, arr+n+1);
        int ans = 0;
        int i1 = 1while (i1 <= n){
            int i2 = i1; while (i2 <= n){
                if (arr[i1].fr.fr == arr[i2].fr.fr){ i2++; } elsebreak; }
            }
            for (int i = i1; i < i2; i++){
                int x = arr[i].fr.sc, c = arr[i].sc;
                //cout << "POS " << arr[i1].fr.fr << "   " << x << ' ' << c << endl;
                auto it = qrr[c].upper_bound({x, N});
                if (it == qrr[c].begin()){ continue; } it = prev(it);
                pi2 p = *it; if (p.fr > x || x > p.sc){ continue; }
                //cout << "QRY " << p.fr << ' ' << p.sc << "   " << qry(p.fr, p.sc) << endl;
                ans = max(ans, qry(p.fr, p.sc));
                qrr[c].erase(it);
                if (p.fr < x){ qrr[c].insert({p.fr, x-1}); }
                if (x < p.sc){ qrr[c].insert({x+1, p.sc}); }
            }
            for (int i = i1; i < i2; i++){ upd(arr[i].fr.sc, +1); }
            i1 = i2; //cout << endl;
        }
        for (int i = 1; i <= m; i++){
            for (pi2 p : qrr[i]){
                //cout << "QRY " << p.fr << ' ' << p.sc << "   " << qry(p.fr, p.sc) << endl;
                ans = max(ans, qry(p.fr, p.sc));
            }
        }
        cout << ans << endl;
        cs.clear(); for (int i = 1; i <= m; i++){ qrr[i].clear(); }
        for (int i = 1; i < N+N; i++){ seg[i] = 0; }
    }
}
cs