[백준] 1300번 - K번째 수 (Gold 2)
업데이트:
문제 링크
문제 설명
N 이하의 두 자연수의 곱으로 쓸 수 있는 자연수 중 K번째로 작은 수를 출력한다.
주의점
제한시간이 2초이고 $N \leq 10^5$이므로, 대략 $2 \times 10^8$ 정도의 연산이 허용된다.
따라서 $O(N^2)$ 이상의 시간복잡도를 가지는 알고리즘을 사용해서는 안 된다.
정답코드 및 설명
M 이하의 자연수 중 N 이하의 두 자연수의 곱으로 쓸 수 있는 자연수의 개수를 $\textrm{underM}(M)$이라 하자(중복을 포함하여 센다).
$\textrm{underM}(M) \geq k$인 최소의 k는 $\textrm{underM}(M) < k$인 최대의 k에 1을 더한 값과 같다.
$\textrm{underM}(M) \leq k$인 최대의 k를 구해서는 안된다.
반례로, N = 3, k = 7이 있다. $\textrm{underM}(5) = 6$, $\textrm{underM}(6) = 8$인데, 7번째로 작은 수는 5가 아닌 6이다.
이제 $\textrm{underM}(M)$만 계산할 수 있다면 이분탐색을 통해 문제를 해결할 수 있다.
M 이하의 자연수 중 i와 N 이하의 자연수의 곱으로 쓸 수 있는 자연수의 개수는 $\textrm{min}(M/i, N)$이다.
또한, k번째 수는 항상 $\textrm{min}(\lceil\sqrt{k}\rceil^{2}, N)$ 이하이다.
문제 해결에 꼭 필요하지는 않지만, 탐색 범위를 조금이라도 줄일 수 있다.
시간복잡도는 $\textrm{underM}(M)$의 계산에 O(N)이 소요되고, 이분탐색 과정에서 $\textrm{underM}(M)$을 $O(\log N^{2}) = O(\log N)$회 계산하므로 $O(N\log N)$이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.*;
public class Main {
static long N;
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
N = Integer.parseInt(br.readLine());
long k = Integer.parseInt(br.readLine());
bw.write(answer(k) + "");
bw.close();
}
static long answer(long k) {
long i = 0;
while (i * i < k)
i++;
long max = i * i + 1;
long min = 1;
long mid;
while (min + 1 < max) {
mid = (max + min) / 2;
if (underM(mid) >= k)
max = mid;
else
min = mid;
}
if (underM(min) != k)
min++;
return min;
}
static long underM(long m) {
long underM = 0;
for (int i = 1; i <= N; i++) {
underM += m / i <= N ? m / i : N;
}
return underM;
}
}
댓글남기기