11401 이항계수 3
알고리즘(페르마의 소정리, 거듭제곱)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 페르마의 소정리 적용
nCk = n! / k!(n-k)! % 1000000007 에서 A = n!, B = k!(n-k)!, p = 1000000007 이라고 할 때
위 식은 (A * B^(-1)) % p 를 성립
페르마의 소정리는 p가 소수일때, p와 서로소인 a가 있으면, a^p mod p = a mod p 가 성립. -> a^p-1 mod p = 1 mod p 이므로 a^p-1 mod p = 1 이 성립함을 알려주는 공식
여기서 양변에 a를 한번 더 나누면
a^p-2 mod p = a^(-1) 이 성립함을 알 수 있음
따라서 위 이항 계수 식은 다음처럼 변형된다.
(A * B^(-1)) % p = (A * B^(p-2)) % p
2. A = n!과 B = k!(n-k)! 를 구한 후, 거듭제곱 공식으로 B^(p-2)를 구하면 됨.
A = n*...*(n-k+1), B = k! 로 두고 풀어도 됨
-> 처음에 틀렸던 이유는 (k!)^(p-2)가 아니라 (k)^(p-2)로 풀었기 때문
코드
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
#include <iostream>
#define DIVIDE 1000000007
using namespace std;
long long getCoefficient(int n, int k);
long long getPower(long long n, long long k);
int main() {
int n, k;
cin >> n >> k;
cout << getCoefficient(n, k) << endl;
}
long long getCoefficient(int n, int k) {
if (k == 0 || k == n)
return 1;
long long top = 1, bottom = 1, bottom2 = 1;
for (int i = n; i >= 1; i--)
top = (top * i) % DIVIDE;
top %= DIVIDE;ㅁ
for (int i = k; i >= 1; i--)
bottom = (bottom * i) % DIVIDE;
bottom %= DIVIDE;
for (int i = n - k; i >= 1; i--)
bottom2 = (bottom2 * i) % DIVIDE;
bottom2 %= DIVIDE;
bottom = (bottom * bottom2) % DIVIDE;
top %= DIVIDE;
return (top * (getPower(bottom, DIVIDE - 2))) % DIVIDE;
}
long long getPower(long long n, long long k) {
long long result = 1, temp = n;
while (k > 0) {
if (k % 2 == 1) {
result = (result * temp) % DIVIDE;
}
temp = (temp * temp) % DIVIDE;
k /= 2;
}
return result % DIVIDE;
}