이번 시간에는 본 시리즈를 시작하게 된 계기가 된 뤼카의 정리를 한번 살펴보고자 한다. 시작하기에 앞서, 이 정리는 아래 링크를 기반으로 내가 이해한 내용을 덧붙여 작성했음을 밝혀 둔다. 틀린 부분, 내가 잘못 이해한 부분 혹은 논리적 비약이 생기는 부분이 있을 수 있으니 의문점이 생긴다면 댓글로 알려주면 매우매우매우 감사하겠다!
이 식의 경우 이항 계수를 이루는 값이 매우 큰 상황으로, 기존의 방식을 적용하기에 어려움이 따른다. 이제 뤼카의 정리를 적용해서 풀어보자.
n0, m0은 n, m을 p로 나눴을 때의 나머지값이다. 즉 C(m0, n0)은 C(m % p, n % p)과 동일하다.
n_i, m_i는 n, m을 p로 i-1번 나눈 몫을 나눴을 때의 나머지값이 될것이다.
즉 n_i, m_i 모두 0이 될 때까지(이 이후값은 모두 0이 될 것이므로) 위 과정을 반복하면 된다.
while N or K :
ans = (ans * math.comb(N % M, K % M)) % M
N //= M
K //= M
여기서 combination 역시 생각해 볼 점이 있는데, 모든 n_i, m_i는 소수 p값 미만의 음이 아닌 정수이다(p 모듈러로 구해지므로). 본 문제는 단 한번의 조합값을 구하므로 math 라이브러리의 comb를 사용하였지만, 여러 케이스를 처리할 때는 DP 등으로 최적화할 수 있겠다. 가능한 경우의 수는 p^2, 전처리 과정에 최대 O(p^2)가 소요됨을 알 수 있다. 전체 풀이 코드는 다음과 같다.
import math
N, K, M = map(int, input().split())
ans = 1
while N or K :
ans = (ans * math.comb(N % M, K % M)) % M
N //= M
K //= M
print(ans % M)