[백준] 11402번 - 이항 계수 4 [Java]
[백준] 11402번 - 이항 계수 4 [Java]
문제 풀이
자연수 $N$ 과 정수 $K$ 에 대한 이항 계수를 $M$ 으로 나눈 나머지를 구해야 하는데, $N$ 이 최대 $10^{18}$ 이며 $K$ 는 최대 $N$ 인 음이 아닌 정수, $M$ 은 2,000이하의 소수이다. 매우 큰 수로 이루어진 이항 계수를 계산하는 것은 단순한 다이나믹 프로그래밍으로는 불가능한데 $M$ 이 소수이며 $M$ 으로 나눈 나머지를 구해야 한다는 점에서 뤼카의 정리를 활용하면 해결할 수 있다.
뤼카의 정리를 통해 보다 작은 이항 계수의 곱을 모듈러 연산한 결과를 구하는 것으로 치환할 수 있는데, 각 이항 계수를 빠르게 구하기 위해 팩토리얼과 역팩토리얼 배열을 활용했다. 각 배열들을 한 번만 초기화하면 각 이항 계수를 $O(1)$ 에 계산할 수 있다. 주어진 $N$, $K$ 를 $M$ 진법으로 변환한 각 자릿수도 필요한데 이는 모듈러 연산을 통해 끝자리를 구하고 계산한 후 나눗셈 연산으로 끝자리를 날려서 다음 자리로 이동하는 테크닉을 활용했다.
역팩토리얼의 경우 페르마의 소정리로 초항을 구한 후 모듈러 곱셈 역원을 활용한 점화식으로 구할 수 있는데 이때 분할 정복을 이용한 거듭제곱으로 로그 시간 내에 거듭제곱을 구하는 방식을 활용했다. $M$ 이 크지 않아서 상수 시간으로 구하는 간단한 방법으로도 해결은 가능하다.
코드
1. 풀이 [Java]
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
long N = Long.parseLong(st.nextToken());
long K = Long.parseLong(st.nextToken());
int M = Integer.parseInt(st.nextToken());
int[] fact = initFact(M); // 팩토리얼 배열
int[] invFact = initInvFact(M, fact[M - 1]); // 역팩토리얼 배열(모듈러 역원)
long ans = 1;
while (N > 0) {
// N, K를 M진법으로 변환시 자릿수
int ni = (int) (N % M);
int ki = (int) (K % M);
if (ni < ki) {
ans = 0;
break;
}
// 뤼카의 정리
ans = ans * fact[ni] * invFact[ki] * invFact[ni - ki] % M;
// 다음 자릿수로 이동하는 효과
N = N / M;
K = K / M;
}
System.out.println(ans);
}
private static int[] initFact(int M) {
int[] fact = new int[M];
fact[0] = 1;
for (int i = 1; i < M; i++) {
fact[i] = fact[i - 1] * i % M;
}
return fact;
}
private static int[] initInvFact(int M, int a) {
int[] invFact = new int[M];
invFact[M - 1] = modPow(a, M - 2, M); // 역팩토리얼의 초항(페르마의 소정리 활용)
for (int i = M - 2; i >= 0; i--) {
invFact[i] = invFact[i + 1] * (i + 1) % M;
}
return invFact;
}
// a^b % mod를 구하는 메서드(분할 정복을 이용한 거듭제곱)
private static int modPow(int a, int b, int mod) {
if (b == 0) return 1;
int half = modPow(a, b / 2, mod);
if (b % 2 == 0) {
return half * half % mod;
} else {
return half * half % mod * a % mod; // half * half * a가 int형 범위를 넘어갈 수 있어서 미리 mod 수행
}
}
}
풀이 정보
1. 풀이 [Java]
- 101 min
This post is licensed under CC BY 4.0 by the author.


