본문 바로가기

백준/수학

백준 13977 - 이항 계수와 쿼리

728x90

www.acmicpc.net/problem/13977

 

최대 10만 개의 쿼리에 대해 $\binom{n}{r} \ mod \ 10^9 + 7$ 을 구해야 하는 문제이다.

 

N과 K는 400만 이하이기 때문에 이항 계수 DP나 일반적인 반복문 연산으로는 제한 메모리/시간 내로 10만개의 쿼리를 처리할 수 없다.

알아야 할 것

이 문제를 해결하기 위해선 페르마의 소정리를 이용해야 한다.

 

페르마의 소정리 : 소수 p가 있고 정수 a가 p의 배수가 아닐 때 다음이 성립한다.

 

$a^{p} \equiv a \ (mod \ p)$

 

페르마의 소정리를 통해 문제에서 요구하는 것을 빠르게 계산할 수 있다.

방법

일단 이항계수를 제곱식으로 바꾸면 다음과 같이 된다.

 

$\binom{n}{r} = \frac{n!}{(n - r)!r!} = n! \times ((n - r)!r!)^{-1}$

 

이 때 페르마의 소정리 식의 양변에  $a^2$ 만큼 나눠 $a^{p - 2} \equiv a^{-1} (mod \ p)$ 을 유도할 수 있고 이 합동식을 응용하면

 

$((n - r)!r!)^{10^9 - 2} \mod p$

 

다음과 같이 분모의 모듈러를 구할 수 있게 된다.

 

여기에 $O(logn)$ 지수 제곱법을 이용하면 각 쿼리에 대해 빠른 계산이 가능해진다.

 

이제 빠르게 이항계수의 합동을 구하는 방법을 알았으니 남은 일은 DP를 통해 팩토리얼의 모듈러를 미리 계산해 놓고 들어오는 쿼리마다 결과를 출력하도록 하면 된다.

 

전체 코드

더보기
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
#include <iostream>
 
using namespace std;
using ll = long long;
 
ll mod = 1e9 + 7;
ll fact[4000001];
 
ll pomod(ll a, ll b)
{
    if (b == 1)
        return a % mod;
    
    if (b & 1)
        return pomod(a, b - 1* a % mod;
    
    return pomod(a * a % mod, b / 2);
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    fact[0= fact[1= 1;
    
    for (int i = 2; i <= 4000000++i)
        fact[i] = fact[i - 1* i % mod;
        
    int t; cin >> t;
    while (t--)
    {
        int a, b; cin >> a >> b;
        cout << fact[a] * pomod(fact[a - b] * fact[b] % mod, mod - 2) % mod << '\n';
    }
}
cs

 

728x90

'백준 > 수학' 카테고리의 다른 글

백준 1947 - 선물 전달  (0) 2020.11.10
백준 1188 - 음식 평론가  (0) 2020.06.01
백준 1629 - 곱셈  (0) 2020.05.25
백준 10250 - ACM 호텔  (0) 2020.05.20
백준 10422 - 괄호  (0) 2020.05.16