BOJ 25649 Ternary Search

BOJ 25649 Ternary Search

문제 링크:

문제 내용

어떤 수열 a1,a2,,ana_1, a_2, \cdots, a_n이 어떤 인덱스 jj에 대해 a1>a2>>aj<<ana_1 > a_2 > \cdots > a_j < \cdots < a_n 또는 a1<a2<<aj>>ana_1 < a_2 < \cdots < a_j > \cdots > a_n, 즉 감소하다가 증가하는 수열이거나 증가하다가 감소하는 수열이면 이 수열을 유니모달하다고 합니다.

서로 다른 양의 정수로 이루어진 길이 nn의 배열 vv가 주어집니다. 이 배열의 첫 1,2,,n1, 2, \cdots, n개의 수로 이루어진 수열을 유니모달하게 만들기 위해서 인접한 수의 교환 (ii를 골라 viv_ivi+1v_{i+1}을 교환)을 최소 몇 번 해야 하는지 각각 구하세요.

입력

첫 줄에는 nn이 주어집니다. (1n200  0001 \le n \le 200\;000)

다음 nn줄에는 배열 vv의 원소가 순서대로 하나씩 주어집니다. (1vi1091 \le v_i \le 10^9)

출력

nn줄을 출력합니다. ii번째 줄에는 vv의 첫 ii개의 원소를 유니모달하게 만들기 위해 필요한 최소 인접 교환 횟수를 출력합니다.

문제 풀이

스포일러

어떤 배열 vv를 감소하다가 증가하는 수열로 만드는 최소 교환 횟수를 구할 수 있으면, 같은 배열을 증가하다가 감소하는 수열로 만드는 횟수는 vv의 값을 109v10^9 - v로 바꿔서 같은 과정으로 구할 수 있습니다.

vv를 감소하다가 증가하는 수열로 만들기 위해 필요한 교환 횟수는 다음과 같이 분석할 수 있습니다.

  • 먼저 vv의 최댓값 vmv_m은 무조건 전체 vv에서 맨 왼쪽이나 맨 오른쪽으로 이동해야 합니다. 둘 중 최적인 것은 양쪽 중에서 수의 개수가 적은 쪽을 고르는 것입니다.
  • 그 이후에 나머지 값들을 감소하다가 증가하는 아무 수열로 만들면 전체 vv도 감소하다가 증가하는 수열이 되므로, vmv_m은 나머지 원소의 비용에 관여하지 않습니다. 따라서 vmv_m을 제거하고 재귀할 수 있습니다.
  • 이를 다시 정리해 보면, 총 교환 횟수는 각 인덱스 ii에 대해 “viv_i의 왼쪽에 있으면서 viv_i보다 작은 수의 개수"와 “viv_i의 오른쪽에 있으면서 viv_i보다 작은 수의 개수” 중 최솟값의 합이 됩니다.

이제 배열의 오른쪽 끝에 값을 추가하면서 위의 값을 모두 얻는 방법을 생각해 봅시다.

viv_i의 왼쪽에 있으면서 viv_i보다 작은 수의 개수는 한 번 결정되면 변하지 않습니다. viv_i오른쪽에 있으면서 viv_i보다 작은 수의 개수는 그 수보다 작은 수가 들어올 때마다 1 증가하는데, 이 값이 왼쪽 값 이상이 되면 viv_i에서의 최적값은 더 이상 업데이트되지 않습니다.

따라서, 0과 1을 저장하는 합 세그먼트 트리를 만들고, 이전 배열의 답에 새로운 수보다 큰 수의 개수를 세어 더해주는 방법을 사용할 수 있습니다. 크기 순으로 jj번째 수가 추가될 때 세그먼트 트리의 jj번째 원소를 1로 만들고, j+1j+1번째부터 끝까지의 합을 더해준 뒤, 더 이상 업데이트가 없는 원소들을 다시 0으로 만들어 주면 됩니다.

각 원소에 대해 업데이트를 멈춰야 하는 인덱스는, 별도의 합 세그먼트 트리에서 크기 순으로 값을 추가하면서 viv_i의 왼쪽 개수를 구한 뒤에, 그 개수의 2배가 되는 지점을 세그먼트 트리 워크로 찾아서 저장해 두면 됩니다.

이 모든 과정을 구현하면 전체 알고리즘은 O(nlogn)\mathcal{O}(n \log n)에 동작합니다. 시간 제한이 꽤 빡빡하므로 비효율적인 구현이 들어가지 않도록 주의합니다.

Last updated on