Convex hull trick은 여러 개의 일차함수들이 있을 때, 어떤
좌표에서의 최댓값 혹은 최솟값을 빠르게 구하기 위해 사용할 수 있는 방법이다. CHT라고 부르기도 한다.
일차함수는 다양한 특징을 가져서 비교적 쉽게 관리할 수 있고, 그래서 문제에도 많이 나온다. 일차함수의 최댓값 문제에서 중요하게 사용되는 성질들은 다음과 같다.
1. 여러 일차함수들이 있을 때, 이들의 최댓값을
라고 하면, 각 일차함수
에 대해
인
값은 닫힌 구간(
을 포함)으로 표현된다.
증명 생략.
2. 일차함수는 볼록하다.
이므로 볼록하면서 오목하다.
3. 일차함수들의 최댓값은 볼록하다. 일반적으로 볼록함수들의 최댓값은 볼록하다.
,
를 만족하는 두 볼록함수가 있다고 하자.
![]()
![]()
위의 두 식에 의해
이므로 볼록하다.
볼록하다는 말은 엄밀하지 않게는 기울기가 증가한다고 말할 수도 있다. 최댓값에 관여되는 일차함수들을 기울기 순서대로 늘어놓으면 각 일차함수들이 관여하는 구간들이 증가하게 되고, 일차함수들을 기울기 순서대로 일렬로 늘어놓고 관리하면 이분탐색을 통해 특정
값에서의 최댓값을 찾을 수 있다.
이 성질들을 이용하여 다음과 같은 Convex hull trick을 생각할 수 있다. 뒤에서 나오는
은 삽입 연산의 횟수,
는 최댓값 쿼리의 개수이다.
A. Incremental convex hull trick
일차함수의 삽입 연산과
에서의 최댓값 쿼리가 있다고 하자.
일차함수들을 기울기 순서대로 배열에 넣어서 관리한다고 생각하자. 단,
인
가 없는
는 무효하기 때문에 없는 것 취급한다.
새로운 일차함수를 삽입한다면, 삽입되는 일차함수의 기울기를 기준으로 삽입될 위치를 결정할 수 있다. 삽입 후의 일차함수 배열을
라고 하자. 다음과 같은 경우가 생길 수 있다.
1.
가 무효하다.
가
보다 작다면
는 무효하다. 이 때는 삽입을 하지 않아야 한다.
2.
이나
이 무효하다.
의 삽입으로 인해 바로 옆의 일차함수가 무효하게 된 경우이다. 이 경우에는 그 일차함수를 지운 후
를 다시 삽입한다. 이 경우에 무조건 일차함수가 1개씩 사라지므로 amortized
번의 연산만이 사용된다.
3.
,
,
이 모두 무효하지 않다.
세 일차함수가 모두 유효하다면,
가 다른 일차함수들에는 영향을 미치지 않는다. 따라서 그 자리에 그대로 삽입할 수 있다.
이 배열을 BBST로 관리한다면 2번 연산을 amortized
, 나머지 연산을
에 처리할 수 있다.
에서의 최댓값 쿼리도
에 처리할 수 있다.
이 방법은 어떤 일차함수가 들어오던 온라인으로 삽입하고 쿼리하는 것이 가능하다. 그러나 BBST의 특성상
이지만 느리고 복잡하다. 또 제거가 불가능하다는 문제점이 있다. 구현을 단순하게 하기 위해서 삽입되는 일차함수나 쿼리로 들어오는
좌표에 제한 조건을 둘 수 있다.
B. Incremental convex hull trick (increasing query)
쿼리로 들어오는
좌표가 단조 증가 또는 감소한다면, 쿼리를 좀 더 간단하게 구현할 수 있다.
좌표가 증가한다는 의미는
보다 작은 쿼리는 더 이상 들어오지 않는다는 것을 의미한다. 가장 왼쪽의 두 일차함수
,
를 보자.
이 관리하는 구간은
,
가 관리하는 구간은
일 것이다. 만약
이라면
의 값이 최댓값이 된다.
이라면 앞으로의 모든 쿼리에서
이 되고,
은 더 이상 사용되지 않게 된다. 따라서
을 지워버리고 위의 과정을 반복할 수 있다.
이 때의 쿼리당 시간복잡도는
일 때
이고,
일 때 일차함수가 무조건 하나 사라지므로 amortized
이다. 총 시간복잡도
로 처리할 수 있다.
C. Incremental convex hull tick (increasing slope)
삽입되는 일차함수의 기울기가 단조 증가 또는 감소한다면, BBST 없이도 구현이 가능하다.
의 삽입 연산이 들어왔다면, 이미 삽입된 일차함수들의 기울기는
의 기울기 이하일 것이다. 가장 기울기가 큰 일차함수가 관리하는 구간은
가 될 것이고, 따라서 새로 추가된 일차함수에 의해 제거되는 일차함수들은 오른쪽의 몇 개일 것이다. 가장 오른쪽에 있는 일차함수 두 개와 새로 삽입될 일차함수를 비교하여 가장 오른쪽에 있는 일차함수가 무효하다면 그 일차함수를 제거하고 반복하면 되고, 무효하지 않다면 가장 오른쪽에 일차함수를 삽입하면 된다. 이것은 스택을 이용하여 관리할 수 있고, 일차함수 삽입의 시간복잡도는 amortized
이 되고, 쿼리의 시간복잡도는
이 된다.
이분탐색을 통해 오른쪽에서 제거될 일차함수의 개수를
에 찾아서 한 번에 제거할 수도 있다. 이것을 배열을 이용하여 구현하면 amortized가 붙지 않는 대신 일차함수 삽입의 시간복잡도가
이 된다.
D. Persistent incremental convex hull trick
A, B, C의 BBST나 스택을 persistent하게 구현하여 사용할 경우, CHT를 persistent하게 사용할 수 있다.
E. Offline dynamic convex hull trick
일차함수의 제거를 처리하기 위해서 오프라인으로 segment tree를 이용할 수 있다.
segment tree를 시간 기준으로 만들면 각 노드들은
를 관리하는 노드가 될 것이다. 각각의 노드들을 A, B, C와 같은 자료구조로 만들어 각각이 CHT를 처리할 수 있도록 만든다.
오프라인 알고리즘이기 때문에 각각의 일차함수들이 관여하는 시간을 구간
로 표현할 수 있다.
에 포함되는 가장 높은
개의 노드에 해당 일차함수를 삽입하면 각 노드의 시간복잡도
에 삽입을 처리할 수 있다.
쿼리 또한 쿼리가 들어온 시간
를 포함하는
개의 노드들에 쿼리를 해서 모두 합쳐주면 각 노드의 시간복잡도
에 쿼리를 처리할 수 있다.
segment tree를 시간 기준으로 만드는 대신
좌표 기준으로 만들 경우 일차함수(직선)이 아닌 선분의 삽입을 처리하는 것도 가능한 등, segment tree를 통해 다양한 문제를 해결할 수 있다.
F. Li-Chao tree
Convex hull trick 글에 썼지만 Convex랑 관련이 없다…
Li-Chao segment tree라고도 부르는 것 같다. 이름에서 볼 수 있듯 segment tree의 일종인데, 각 노드들은
좌표
를 관리하며, 하나의 일차함수를 가지고 있다.
Li-Chao tree가 사용될 수 있는 상황은 쿼리로 들어오는
좌표가 유한한 경우로, 보통 쿼리들을 모두 알고 있거나, 쿼리가 범위가 정해진 정수일 때 사용할 수 있다.
최댓값을 구하고자 할 때, Li-Chao tree의 초기 상태는 모든 노드가
를 가지고 있는 상태이다. 이 트리에서 쿼리
에서의 최댓값을 구하기 위해서는
를 포함하는
모든 노드에서의
값의 최댓값을 가져오면 된다.
이제 Li-Chao tree가 위의 성질을 유지하도록 새로운 일차함수를 삽입하자.
를 관리하는 노드에
가 삽입된다고 가정하자. 일차함수가 관리하는
가 구간으로 나타나기 때문에 노드에 있던 일차함수와 새로운 일차함수 중에서 단 하나의 일차함수가 관리하는 구간만이
을 포함한다.
가 관리하는 구간에 포함된다는 뜻은, 나머지 하나의 일차함수는
의 왼쪽이나 오른쪽 중 하나만을 관리한다는 의미가 된다. 따라서 나머지 하나의 일차함수가 관리하는 구간은
또는
에 포함된다.
에 포함될 경우에는 왼쪽 자식 노드,
에 포함될 경우에는 오른쪽 자식 노드에 재귀적으로 삽입해줄 수 있다.
계속해서 재귀적으로 삽입하면 쿼리로 가능한
좌표의 유한성으로 인해
인 노드가 나타나고, 원래 있던 일차함수와 새로 삽입된 일차함수 중 하나가 무효해지는 순간이 발생하게 된다. 이 때 재귀를 중지할 수 있다.
좌표로 가능한 경우의 수를
라고 할 때, Li-Chao tree의 시간복잡도는 삽입, 쿼리가 각각
가 된다. Li-Chao tree도 E의 segment tree를 이용한 것과 똑같이 선분을 삽입하는 것이 가능한데, 이 때 시간복잡도는
가 된다.
일 때 A부터 E까지의 방법은 두 일차함수의 교점을 비교하는 과정이 필요하다. 교점을 정확히 비교하기 위해 정수 자료형을 사용할 경우 자료형의 범위가
를 포함해야 하는데, 많은 DP 문제에서
,
의 범위가 나와서 어쩔수 없이 실수 자료형을 사용하게 된다. 그러나 Li-Chao tree는 함수의 함수값만을 비교하기 때문에 실수 자료형을 사용하게 되는 경우가 잘 발생하지 않는다.
Li-Chao tree도 segment tree의 일종이기 때문에 동적으로 구성할 수 있다. Li-Chao tree의 특이한 점은 동적으로 트리 압축 없이 구성해도 공간복잡도가
라는 것이다.
답글 남기기