https://en.wikipedia.org/wiki/Lucas%27s_theorem
Lucas's theorem - Wikipedia
From Wikipedia, the free encyclopedia In number theory, Lucas's theorem expresses the remainder of division of the binomial coefficient ( m n ) {\displaystyle {\tbinom {m}{n}}} by a prime number p in terms of the base p expansions of the integers m and n.
en.wikipedia.org
1. Lucas' Theorem
정수 m,n과 소수 p에 대하여 (mn)≡k∏i=0(mini)(modp)
여기서 mi와 ni는 m,n을 p진법 전개했을때 얻을 수 있는 수이다.
구체적으로 m=mkpk+mk−1pk−1+...+m1p+m0
n=nkpk+nk−1pk−1+...+n1p+n0
2. 증명
https://en.wikipedia.org/wiki/Binomial_theorem
Binomial theorem - Wikipedia
From Wikipedia, the free encyclopedia Algebraic expansion of powers of a binomial In elementary algebra, the binomial theorem (or binomial expansion) describes the algebraic expansion of powers of a binomial. According to the theorem, it is possible to exp
en.wikipedia.org
이항정리로부터 다음 식을 얻는다.
(1+x)p=p∑r=0(pr)xr
여기서, (pr)=p∗p−1∗p−2...∗1(p−r)∗(p−r−1)...(1)∗r∗r−1∗r−2∗...∗1
즉, r=0,p를 제외한 모든 r = 1,2,...,p-1에 대하여 (pr)(modp)≡0
그래서, (1+x)p≡1+xp
귀납법으로 음이 아닌 정수 i에 대하여 (1+x)pi≡1+xpi(modp)
https://artofproblemsolving.com/wiki/index.php/Lucas%27_Theorem
Art of Problem Solving
artofproblemsolving.com
여기서 (1+xpk)p를 전개할때는 x를 xpk로 보고 이항전개를 한 것이다.
이제, m의 p진법 전개 m=∑ki=0mipi에 대하여
m∑n=0(mn)xn≡(1+x)m≡(1+x)m0+m1p+m2p2+...mkpk
(1+x)pi≡(1+xpi)를 이용하면 다음과 같다.
k∏i=0(1+x)mipi≡k∏i=0(1+xpi)mi
바로 위에서 제시한 이항전개 테크닉을 이용해서
(1+xpi)mi=mi∑ni=0(mini)xnipi
합의 기호와 곱의 기호는 서로 바꿀 수 있다. f(mi,ni)=(mini)xnipi라고 해보자.
k∏i=0mi∑ni=0f(mi,ni)=k∏i=0(f(mi,0)+f(mi,1)+...+f(mi,mi))
(f(m0,0)+...+f(m0,m0))(f(m1,0)+...+f(m1,m1))...(f(mk,0)+...+f(mk,mk))=∑ijf(m0,i0)f(m1,i1)...f(mk,ik)
∑ijk∏i=0f(mi,ni)=∑ij(k∏i=0(mini))x∑ki=0nipi=m∑n=0(k∏i=0(mini))xn
여기서 n=∑ki=0nipi으로 n에 대한 p진법 전개이다.
그러므로, m∑n=0(mn)xn≡m∑n=0k∏i=0(mini)xn(modp)은 x에 대한 항등식이다.
따라서, xn의 계수 (mn)≡k∏i=0(mini)(modp)
3. 연습문제
11402번: 이항 계수 4
첫째 줄에 N, K와 M이 주어진다. (1 ≤ N ≤ 1018, 0 ≤ K ≤ N, 2 ≤ M ≤ 2,000, M은 소수)
www.acmicpc.net
4. 풀이
m진법 전개를 하면 이항계수의 n,k가 m으로 나눈 나머지 0,1,...,m-1로 바뀌므로 factorial을 구하기 용이하다.
n,k가 매우 크고 m이 충분히 작은 이 문제와 같은 경우 뤼카의 정리가 유용하다.
즉 factorial로 필요한건 m으로 나눈 나머지인 0,1,2,...,m-1까지이다.
이를 다이나믹 프로그래밍으로 미리 구해둔다.
당연히 factorial을 m으로 나눈 나머지를 저장해두는 것이 조금이나마 유리하다.
n,k,m = map(int,input().split())
factorial = [0]*m
factorial[0] = 1
for i in range(1,m):
factorial[i] = factorial[i-1] * i
factorial[i] %= m
다음에 필요한건 n,k의 m진법 전개이다.
m진법 전개는 2진법으로 표현할때 2로 나눈 몫,나머지를 반복적으로 구하고 나머지만 모아서 쓰던걸 기억한다면...
m으로 나눈 몫, 나머지를 반복적으로 구하고 나머지만 챙기면 된다.

n을 m으로 나눈 몫이 n//m이고 n%m인데, n%m을 n0라 하고.. n//m을 n으로 갱신해준다.
마찬가지로 k도 k//m을 k라 갱신하고 k%m을 k0라 하고...
먼저 구한 n0와 k0에 대하여, (n0k0)modm을 구해준다.
m이 소수이고 n0와 k0가 m으로 나눈 나머지이므로 서로소여서 페르마의 소정리를 적용할 수 있다.
즉, (n0k0)≡(n0)!((n0−k0)!(k0)!)m−2(modm)
이를 반복해서 구하면 되는데... 언제까지 반복해야하는가?가 고민이다.
특히 n,k는 다른 수이니 m진법 전개를 하면 길이가 다를게 뻔하다..
그러니까 n이 더 크니까 ni는 계속 나오는데 ki는 어느순간 계속 0이 나올게 뻔하다..
그런데 이항계수의 성질 k = 0이면 (nk)=1이므로, 더 큰 수인 n이 0보다 클 때까지 반복하면 될 것이다.
그런데 또 하나 문제는 n이 더 크다고 해서 m으로 나눈 나머지인 ni도 더 크다는 보장은 없다.
하지만 이항계수의 성질 n < k 이면 (nk)=0을 이용하면...
나누는 과정을 반복하다가.. (niki)(modm)을 answer에 곱해나가는데...
(niki)=0을 곱하면... answer은 무조건 0이 될 것이므로... ni<ki이면 answer = 0으로 하고 반복문을 종료한다.
#lucas theorem
n,k,m = map(int,input().split())
factorial = [0]*m
factorial[0] = 1
#m으로 나눈 나머지인 0,1,..,m-1!을 구해준다.
for i in range(1,m):
factorial[i] = factorial[i-1] * i
factorial[i] %= m
answer = 1
#n > 0일때까지 반복해나감
while n > 0:
n,r1 = n//m, n%m
k,r2 = k//m, k%m
#n,k를 m으로 나눈 나머지인 r1,r2에 대해 r1Cr2 mod m을 구해준다.
#m은 소수이고 r1,r2는 m과 서로소이므로 페르마의 소정리를 이용
#그런데, r1 < r2이면 r1Cr2 = 0이므로 바로 반복문을 종료
if r1 >= r2:
answer *= (factorial[r1])*pow(factorial[r1-r2]*factorial[r2],m-2,m)
answer %= m
else:
answer = 0
break
print(answer)
'정수론' 카테고리의 다른 글
n차 다항식 함숫값 빠르게 계산하기(페르마의 소정리로 거듭제곱 빠르게 계산하기) (0) | 2023.07.08 |
---|---|
오일러 phi 함수 재활 -xφ(x) = n을 만족하는 x의 최솟값 구하기- (0) | 2023.07.06 |
조합의 곱의 합을 구하는 Vandermonde's identity (0) | 2023.07.03 |
약수의 합이 짝수인지 홀수인지 바로 알 수 있을까?(시그마 함수의 성질 + 제곱수의 개수 바로 구하기) (0) | 2023.06.26 |
팩토리얼을 계산하지 않고도 소인수분해하는 기본기 (0) | 2023.06.24 |