이 문제를 풀기위해 Plain Sweeping, Segment Tree, Coordinate Compression 기법을 사용해야한다.
Plain Sweeping
Plain Sweeping 골자 코드
기본적인 전략을 위 코드가 나타내고 있다. SISOBUS Blog 에 있는 그림을 참고해보자.
X 축 좌표압축
Plain Sweeping 골자 코드
Plainng Sweeping 은 Sweeping 되는 한 축을 따라 가면서 삽입/삭제 연산을 수행하게 된다. 연산이 수행되는 위치와 연산에 관련된 정보 그리고 삽입/삭제인지 나타내는 값을 자료구조로 만들어 좌표압축을 수행하면 모든 범위에 대해 체크를 하지 않아도 되게 된다.
삽입 삭제 최적화
특정 범위의 Mask 값을 하나하나 체크하고 증감시키면 너무 느리다. 이를 위한 자료구조가 Lazy Segment Tree 이다. 세그먼트 트리의 중간노드가 수정해야 할 범위에 포함된다면 굳이 자식노드까지 가지 않음으로써, Leaf Node 까지 탐색하는 횟수가 많아봐야 범위 양쪽에서 수행되는 2번으로 그치게 된다. 그래서 시간복잡도가 선형이 아닌 로그가 되는 것이다.
보통 이런 Lazy Segment Tree 는 Propagation 연산을 수행하지만 여기선 그렇지 않다. 왜냐하면 우리는 Mask 값이 1 이상인 범위를 구해야하기 때문이다. 만약 Propagation 을 수행한다면 중간노드의 Mask 값만으로 자식노드들이 모두 Mask 되어 있는지 알 수 없어진다. 일부 자식 노드에서만 Mask 값이 몰려있는지, 자식 노드들에 고르게 Mask 값이 들어있는지 알 도리가 없기 때문이다.
대신 중간 노드에서 Mask 를 수정만 한다면 자식 노드에 대한 정보는 바로 알 수 있다. Mask 값이 1 이상이면 자식노드들은 모두 Mask 가 1 이상이다. 만약 0 이면 자식노드에서의 범위를 가져오면 된다. 이를 재귀적으로 수행하면 로그시간단위로 Mask 값을 수정하고 및 범위의 크기를 알 수 있다.
Y 축 좌표압축
이 문제는 Y 축 범위가 매우 크므로 역시 좌표압축을 해주어야한다. 그런데 세그먼트 트리에서의 좌표압축은 꽤나 헷갈리는 부분이 많다.
위 세그먼트 트리의 구현은 [s, e) 가 아닌 [s, e] 범위에 대해 수행된다. 좌표압축 전에는 Leaf Node 가 균등하게 길이 1을 나타냈으므로 [s, e] 가 나타내는 길이는 e - s + 1 이 된다. 하지만 좌표압축이 되면 균등함이 보장되지 않아 이렇게 계산할 수 없다.
대신 ys[e] - ys[s-1] 이거나 ys[e+1] - ys[s] 중 하나로 계산할 수 있을 것이다. 편의상 범위가 [left, right] 로 들어왔을 때 left 미만으로 값이 빠지지 않도록 ys[e+1] - ys[s] 로 계산하자.
그러면 문제가 생기는 것이 범위의 끝부분에서 초과가 된다는 것이다. 예를들어 ys[r+1] - ys[r] 이렇게 말이다. 그래서 업데이트 시에 right 에서 하나를 빼줘서 사용해야한다.
또한 위 세그먼트 트리는 인덱스를 1 부터 시작하므로 기본적으로 오프셋을 1 을 줘야한다.
댓글남기기