자연수 N과 정수 K가 주어졌을 때 이항 계수 (N, K)를 1,000,000,007로 나눈 나머지를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 4,000,000, 0 ≤ K ≤ N)
출력
(N, K)를 1,000,000,007로 나눈 나머지를 출력한다
풀이
이항계수는 nCk로 쉽게 구할 수 있는데, 곱셈간의 나머지를 구하기는 쉽지만 나눗셈이 포함 될 경우 나머지를 계산하기가 어렵기 때문에 페르마의 소정리를 이용해서 풀었다.
페르마의 소정리 증명
$$a^{p-2} \equiv a^{-1} \pmod{p}$$
페르마의 소정리를 참고하면 위와 같이 나타낼 수 있다.
그러므로 nCk를 n!/a 로 표현하고 a부분을 페르마의 소정리를 이용해 나머지를 구하면 된다.
즉,
$$\frac{n!}{a} \pmod{p} \equiv n!*a^{p-2} \pmod{p}$$
라는 것이다.
이 때 a = (n-k)!*k! 이다. a를 p-2번 제곱한 수의 나머지를 구하는 방법은 절반씩 나눠가며 재귀로 구했다.
Code
#include<iostream>
using namespace std;
// 11401
const long long mod_num = 1000000007;
long long N, K;
long long binary_mod(long long num, int cnt) {
if (cnt == 1) {
return num % mod_num;
}
int mid = cnt / 2;
long long c = binary_mod(num, mid);
long long renum = 0;
if (cnt % 2==1) {
renum = c * c;
renum%=mod_num;
renum *= num;
return renum%=mod_num;
}
else {
renum = c * c;
return renum %= mod_num;
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cin >> N >> K;
//A=n!, B=(n-k)!*k!
long long A=1;
long long B=1;
for (int i = 1; i <= N; i++) {
A *= i;
A = A % mod_num;
}
for (int j = 1; j <= K; j++) {
B *= j;
B %= mod_num;
}
for (int j = 1; j <= N-K; j++) {
B *= j;
B %= mod_num;
}
long long ans = binary_mod(B, mod_num - 2);
ans = ans * A;
cout << ans % mod_num;
return 0;
}
이 문제는 다시 풀라고 해도 페르마의 소정리를 가져다 주면서 이걸 활용해서 풀어라 라고 말하지 않는 이상 바로 푸는 방법이 생각나지 않을 것 같다;
반응형
'Algorithm > BAEKJOON' 카테고리의 다른 글
[백준/Python] 11444번 - 피보나치 수 6 (0) | 2024.01.04 |
---|---|
[백준/C++] 10830번 - 행렬 제곱 (1) | 2024.01.04 |
[백준/C++] 1629번 - 곱셈 (1) | 2024.01.04 |
[백준/C++] 25682번 - 체스판 다시 칠하기 2 (1) | 2024.01.04 |
[백준/C++] 1780번 - 종이의 개수 (0) | 2024.01.04 |