BJ 11042 이항 계수 4

이경헌·2021년 1월 12일
0

백준 - 이항 계수

목록 보기
4/8

https://www.acmicpc.net/problem/11402

Lucas Theorem

뤼카의 정리는 어떤 이항 계수를 소수 pp로 나눈 나머지를 구할 때 유용하게 쓸 수 있는 방법입니다.

임의의 음이 아닌 정수 nnkk, 그리고 소수 pp에 대해 다음이 성립합니다.

(nk)i(niki)(modp){n\choose k}\equiv\prod_i {n_i\choose k_i}\pmod p

이 때 nin_ikik_i는 각각 nnkkpp-진 전개에서의 자리수입니다. 즉,

n=ntnt1n1(p)k=ktkt1k1(p)\begin{aligned} n&={n_tn_{t-1}\ldots n_1}_{(p)} \\ k&={k_tk_{t-1}\ldots k_1}_{(p)} \end{aligned}

입니다.

접근 방법

10^18 까지의 큰 수이니 O(logn)O(\log n)의 방법을 사용하는 것이 마땅할 것 같습니다. 주어진 nk를 각각 m진 전개하는데 소요되는 시간은 약 logmn\log_m{n}, logmk\log_m{k}입니다. 이들을 곱하는 것은 바로 수행 가능합니다. 이제 각 nin_ikik_i에 대해 이들의 이항 계수를 구하는 방법인데, 이전 글에서의 방법을 이용해 O(logn)O(\log n)의 시간복잡도로 계산할 수 있습니다.

코드

n, k, m = map(int, input().split())

def pow(n, k, m):
    if k == 1:
        return n
    pow_half = pow(n, k//2, m)
    if k % 2 == 0:
        return (pow_half ** 2) % m
    else:
        return (pow_half ** 2 * n) % m

factorial = [1] * (m+1)
for idx in range(2, m+1):
    factorial[idx] = (factorial[idx-1] * idx) % m

def inverse(n):
    return pow(factorial[n], m-2, m)

mult = 1
while n != 0 or k != 0:
    n_digit = n % m
    n //= m
    k_digit = k % m
    k //= m

    if n_digit < k_digit:
        mult = 0
        break
    else:
        mult *= factorial[n_digit] * inverse(n_digit - k_digit) * inverse(k_digit)
        mult %= m

print(mult)
profile
Undergraduate student in Korea University. Major in electrical engineering and computer science.

2개의 댓글

comment-user-thumbnail
2021년 10월 22일

안녕하세요. 양질의 글 잘 읽었습니다. 백준 23260 번에 같은 로직을 적용 해볼려고 시도 중인데요 숫자가 10^9+7 까지다 보니 factorial에서 메모리가 초과 되어 버리네요. 메모리를 적게 써서 팩토리얼을 구하고 메모이제이션 하는 방법은 없을까요?

1개의 답글