BOJ 25649 Ternary Search
문제 내용
어떤 수열 이 어떤 인덱스 에 대해 또는 , 즉 감소하다가 증가하는 수열이거나 증가하다가 감소하는 수열이면 이 수열을 유니모달하다고 합니다.
서로 다른 양의 정수로 이루어진 길이 의 배열 가 주어집니다. 이 배열의 첫 개의 수로 이루어진 수열을 유니모달하게 만들기 위해서 인접한 수의 교환 (를 골라 와 을 교환)을 최소 몇 번 해야 하는지 각각 구하세요.
입력
첫 줄에는 이 주어집니다. ()
다음 줄에는 배열 의 원소가 순서대로 하나씩 주어집니다. ()
출력
총 줄을 출력합니다. 번째 줄에는 의 첫 개의 원소를 유니모달하게 만들기 위해 필요한 최소 인접 교환 횟수를 출력합니다.
문제 풀이
스포일러
어떤 배열 를 감소하다가 증가하는 수열로 만드는 최소 교환 횟수를 구할 수 있으면, 같은 배열을 증가하다가 감소하는 수열로 만드는 횟수는 의 값을 로 바꿔서 같은 과정으로 구할 수 있습니다.
를 감소하다가 증가하는 수열로 만들기 위해 필요한 교환 횟수는 다음과 같이 분석할 수 있습니다.
- 먼저 의 최댓값 은 무조건 전체 에서 맨 왼쪽이나 맨 오른쪽으로 이동해야 합니다. 둘 중 최적인 것은 양쪽 중에서 수의 개수가 적은 쪽을 고르는 것입니다.
- 그 이후에 나머지 값들을 감소하다가 증가하는 아무 수열로 만들면 전체 도 감소하다가 증가하는 수열이 되므로, 은 나머지 원소의 비용에 관여하지 않습니다. 따라서 을 제거하고 재귀할 수 있습니다.
- 이를 다시 정리해 보면, 총 교환 횟수는 각 인덱스 에 대해 “의 왼쪽에 있으면서 보다 작은 수의 개수"와 “의 오른쪽에 있으면서 보다 작은 수의 개수” 중 최솟값의 합이 됩니다.
이제 배열의 오른쪽 끝에 값을 추가하면서 위의 값을 모두 얻는 방법을 생각해 봅시다.
의 왼쪽에 있으면서 보다 작은 수의 개수는 한 번 결정되면 변하지 않습니다. 의 오른쪽에 있으면서 보다 작은 수의 개수는 그 수보다 작은 수가 들어올 때마다 1 증가하는데, 이 값이 왼쪽 값 이상이 되면 에서의 최적값은 더 이상 업데이트되지 않습니다.
따라서, 0과 1을 저장하는 합 세그먼트 트리를 만들고, 이전 배열의 답에 새로운 수보다 큰 수의 개수를 세어 더해주는 방법을 사용할 수 있습니다. 크기 순으로 번째 수가 추가될 때 세그먼트 트리의 번째 원소를 1로 만들고, 번째부터 끝까지의 합을 더해준 뒤, 더 이상 업데이트가 없는 원소들을 다시 0으로 만들어 주면 됩니다.
각 원소에 대해 업데이트를 멈춰야 하는 인덱스는, 별도의 합 세그먼트 트리에서 크기 순으로 값을 추가하면서 의 왼쪽 개수를 구한 뒤에, 그 개수의 2배가 되는 지점을 세그먼트 트리 워크로 찾아서 저장해 두면 됩니다.
이 모든 과정을 구현하면 전체 알고리즘은 에 동작합니다. 시간 제한이 꽤 빡빡하므로 비효율적인 구현이 들어가지 않도록 주의합니다.