본문 바로가기

문제풀이/백준

백준 9244 핀볼

https://www.acmicpc.net/problem/9244

 

선분끼리 서로 만나지 않는다는 조건 때문에 시작 위치가 결정되면 떨어지는 과정이 확정된다. 공이 떨어지는 과정을 시뮬레이션할 수 있을지 생각해보자. 선분은 일차함수의 조각이니 처음에는 리차오 트리를 쓰고 싶다는 생각이 들었다. 선분이 교차하지 않으니 공이 어떤 선분에 도달했을 때 하나의 x에서라도 도착한 선분보다 위에 있는 선분은 절대 탈 일이 없고, 이러한 위쪽 선분들은 다 지울 수 있다. 따라서 선분을 리차오에 잘 저장하고 원하는 선분을 잘 삭제할 수 있으면 풀 수 있겠다 싶었다. 

 

처음에는 리차오의 각 노드마다 set을 박아서 원하는 선분 삭제하기 같은 걸 생각했는데, set에 선분 NlogN개 삽입/삭제를 해야 하니 시간이 안 될 것 같았다. 결국 하고 싶은 건 선분을 logN개로 쪼개서 세그의 형태로 저장하는 것이고, 선분이 교차하지 않으니 선분의 상하관계가 바뀌는 일은 없다. 따라서 선분을 쪼개 담은 뒤, 노드마다 각 노드가 담당하는 x 범위에 들어가는 아무 x좌표를 골라 해당 점에서의 함수값 기준으로 선분을 정렬할 수 있다. 모든 노드에 대해 속한 선분을 정렬하면 f(x) >= y인 모든 선분 지우기 같은 연산을 해당 노드가 담고 있는 선분 중 가장 큰 거 몇 개 지우기 같은 식으로 처리할 수 있게 된다.

 

요약하면 아래 방법으로 문제를 풀 수 있다.

1. 세그먼트 트리의 방식으로 직선을 최대 logN개로 나누어 저장한다.

2. 세그트리의 각 노드에 저장된 직선을 함숫값 기준으로 정렬한다. 선분이 교차하지 않으니 어떤 x를 잡아도 정렬 결과는 동일하다.

3. 초기 좌표 (x0, inf)에서부터 다음 작업을 반복한다. 각 단계에서 현재 좌표를 (x, y)라고 두자.

3-1. x를 지나는 선분 중 x에서의 함숫값이 y 이상인 선분을 모두 지운다.

3-2. x를 지나고 x에서의 함숫값이 y 미만인 선분 중 가장 위에 있는 선분을 찾는다. 

3-3. (x, y)를 찾은 선분의 끝점으로 갱신하고, 찾은 선분보다 위에 있는 선분을 모두 지운다.

 

3-1, 3-2 과정은 x를 포함하는 logN개의 노드를 확인하여 수행할 수 있다. 3-3에서 위에 있는 선분을 모두 지우는 작업은 선분이 저장된 모든 노드에서 찾은 선분보다 정렬 순서가 뒤인 선분을 모두 지우는 식으로 할 수 있다. 어떤 선분을 지울 때, 해당 선분이 저장된 모든 노드를 다 보며 해당 선분보다 정렬 순서가 뒤인 선분을 연쇄적으로 지워주어야 할 것이다.

 

함숫값을 분수 형태로 관리했는데, 약분하는 과정이 시간을 많이 잡아먹어서 TLE를 받았다. 어차피 오버플로우는 안 나고, 약분을 굳이 하지 않으니 빠르게 풀 수 있었다. 그 밖에도 이것저것 자잘한 실수를 많이 했다. 요새 2000B 이상 짜면 무조건 디버깅으로 시간을 한참 잡아먹는 것 같은데.. 코드 설계를 구체적으로 안 하고 무지성코딩으로 들어가서 어쩔 수 없는 것 같다. 대회를 위해 코드 설계 연습이 필요하다고 생각한다.

 

사실 좀 뇌절을 많이 한 것 같다. https://leo630.tistory.com/143 에서 설명하는 스위핑 풀이가 훨씬 아름답고 깔끔하다고 생각한다. 

 

똑같이 Class 9인 Meteor Shower 문제도 비슷하게 풀릴 것 같은데,, 이건 좀 더 생각해봐야겠다.

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
 
struct Line {
    ll x1, y1, x2, y2;
    pll get(int x) {
        return {y2*(x-x1) + y1*(x2-x), x2-x1};
    }
} L[101010];
vector<int> idxs[101010];
vector<int> seg[202020 << 2];
pii intv[202020 << 2];
pll tmp[101010];
int N, chk[101010];
 
// return a <= b
int cmp(pll a, pll b) {
    return a.first*b.second <= a.second*b.first;
}
 
void add(int now, int l, int r, int s, int e, int i) {
    if (r < s or e < l) return;
    if (s <= l and r <= e) {
        idxs[i].push_back(now);
        seg[now].push_back(i);
        return;
    }
    int m = l+>> 1;
    add(now*2, l, m, s, e, i);
    add(now*2+1, m+1, r, s, e, i);
}
 
void dfs(int now, int l, int r){
    sort(seg[now].begin(), seg[now].end(), [&](int a, int b) {
        pll fa = L[a].get(l), fb = L[b].get(l);
        return fa.first*fb.second < fa.second*fb.first;
    });
    intv[now].first = l, intv[now].second = r;
    if (l == r) return;
    int m = l+>> 1;
    dfs(now*2, l, m);
    dfs(now*2+1, m+1, r);
}
 
int main() {
    cin.tie(nullptr)->sync_with_stdio(0);
 
    cin >> N;
    vector<int> xc;
    for (int i = 1; i <= N; i++) {
        auto& [xs, ys, xe, ye] = L[i];
        cin >> xs >> ys >> xe >> ye;
        if (xs > xe) swap(xs, xe), swap(ys, ye);
        xc.push_back(xs);
        xc.push_back(xe);
    }
    ll x, y = 1e9;
    cin >> x;
    xc.push_back(x);
    sort(xc.begin(), xc.end());
    xc.erase(unique(xc.begin(), xc.end()), xc.end());
    for (int i = 1; i <= N; i++){
        L[i].x1 = lower_bound(xc.begin(), xc.end(), L[i].x1) - xc.begin() + 1;
        L[i].x2 = lower_bound(xc.begin(), xc.end(), L[i].x2) - xc.begin() + 1;
        add(11, xc.size(), L[i].x1, L[i].x2, i);
    }
    dfs(1,1,xc.size());
    x = lower_bound(xc.begin(), xc.end(), x) - xc.begin() + 1;
    while (1) {
        int now = 1, l = 1, r = xc.size(), nxtL = 0;
        vector<int> q;
        now = 1, l = 1, r = xc.size();
        while (1) {
            while (seg[now].size()){
                int ni = seg[now].back();
                pll yy = L[ni].get(x);
                if (y*yy.second < yy.first) {
                    if (!chk[ni]) {
                        chk[ni] = 1;
                        q.push_back(ni);
                    }
                    seg[now].pop_back();
                }
                else {
                    if (!nxtL or cmp(L[nxtL].get(l), L[ni].get(l))) 
                        nxtL = ni;
                    break;
                }
            }
            if (l == r) break;
            int m = l+>> 1;
            if (x <= m) now = now*2, r = m;
            else now = now*2+1, l = m+1;
        }
        if (!nxtL) break;
        if (!chk[nxtL]) {
            q.push_back(nxtL);
            chk[nxtL] = 1;
        }
        if (L[nxtL].y1 < L[nxtL].y2) x = L[nxtL].x1, y = L[nxtL].y1;
        else x = L[nxtL].x2, y = L[nxtL].y2;
        while (q.size()) {
            int i = q.back();
            q.pop_back();
            for (int segi : idxs[i]) {
                int nx = intv[segi].first;
                while (seg[segi].size() and cmp(L[i].get(nx), L[seg[segi].back()].get(nx))){
                    int li = seg[segi].back();
                    if (!chk[li]){
                        chk[li] = 1;
                        q.push_back(li);
                    }
                    seg[segi].pop_back();
                }
            }
        }
    }
    cout << xc[x-1];
}
cs

 

'문제풀이 > 백준' 카테고리의 다른 글

백준 3319 전령들 + Class 9  (2) 2026.03.08
백준 13329 Meteor Shower  (0) 2026.03.07
백준 31603 Tree Quiz  (0) 2026.02.28
백준 32991 폭죽놀이  (0) 2026.02.21
백준 8217 유성  (0) 2025.12.21