문제 링크
- http://icpc.me/19088
사용 알고리즘
- 세그먼트 트리
시간복잡도
- $O(Q log N)$
풀이
현재 갖고 있는 수들로 만들 수 있는 가장 큰 수는 현재 갖고 있는 수를 내림차순한 결과입니다.
세그먼트 트리의 i번째 리프노드에 현재 갖고 있는 i의 개수를 저장합니다. 만약 5를 3개 갖고 있다면 555
, 4개 갖고 있다면 5555
처럼 표현해야 합니다. 이를 편하게 하기 위해서 다음과 같은 배열을 정의합시다.
- one[i] = D진법 상에서 1이 i개 있는 수를 1e9+7로 나눈 나머지
이런 배열을 알고 있으면 a가 b개 있는 수를 a × one[b] mod 1e9+7
로 구할 수 있습니다.
리프노드는 쉽게 표현 가능하지만, 두 자식 정점의 값을 합쳐서 부모 정점을 만드는 것은 조금 복잡해 보입니다. 부모 정점의 값은 양쪽 자식에 숫자가 몇 개 있는지 있는지, 양쪽 자식이 어떤 수를 표현하는지에 따라 달라지게 됩니다.
왼쪽 자식 정점 L과 오른쪽 자식 정점 R을 합쳐서 부모 정점 P를 만드는 방법을 알아봅시다.
가장 큰 수를 만들기 위해서는 큰 숫자가 앞에 나와야 합니다. 그리고 큰 숫자는 오른쪽 자식에 있습니다. 그러므로 P가 표현하는 값은 R이 표현하는 값 뒤에 L이 표현하는 값이 따라오는 형태가 됩니다. R이 표현하는 값 뒤에 0을 적당히 붙인 뒤 L이 표현하는 값을 더하면 되므로, 아래 배열을 미리 구해놓읍시다.
- pw[i] = $D^i \mod 1e9+7$
왼쪽 정점에 있는 숫자의 개수를 ls, 오른쪽 정점에 있는 숫자의 개수를 rs, 왼쪽 정점이 표현하는 수를 lv, 오른쪽 정점이 표현하는 수를 rv라고 한다면, P가 표현하는 수는 아래와 같이 계산할 수 있습니다.
rv × pw[ls] + lv
위 식은 rv 위에 lv의 길이만큼 0을 붙이고 lv를 더한 값을 의미합니다.
세그먼트 트리를 갱신해주면서 값을 잘 관리해주면 됩니다.
수의 범위가 최대 10억인데, 동적 세그먼트 트리를 만들거나 좌표 압축을 해주면 메모리를 아낄 수 있습니다.
전체 코드
1 |
|