ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • BOJ 11439: 이항 계수 5
    공부(Archive)/BOJ 2019. 7. 10. 18:54

    올해들어 알고리즘 공부를 뜸하게 하다가 최근 일주일에 몇 문제씩은 풀게되었다.

     

    이항 계수 4 문제까지는 구글링을 통해 다양한 공식과 정리들을 알게되어 쉽게 해결하였지만 최근 도전했던 이항 계수 5는 도저히 어떠한 공식도 정리도 찾을수가 없었다.

     

    내가 이 문제를 풀기 위해 어떤 방식으로 접근했는지와 결국 풀이를 떠올리고 풀어낸 방법을 소개하고자 한다.

     

    1. 먼저 N제한이 400만이기 때문에 DP를 사용하기에는 너무나도 큰 메모리 공간이 필요하기 때문에, 아마 메모리 공간이 부족하지 않더라도 너무나도 긴 시간이 걸릴 것이기 때문에 불가능하다고 생각했다.

     

    2. 이항 계수 4를 풀기 위해 사용했던 Lucas Theorem을 사용해보고자 했지만 나누는 수가 소수인 경우에만 사용할 수 있으므로 포기했다.

     

    3. 그렇다면 그냥 팩토리얼을 계산하여 모듈러 역원을 구해 답을 구해보고자 했다.

     3-1. 먼저 Fermat's little theorem을 이용하여 모듈러 역원을 구해보자 했지만 두 수가 서로소가 아닌 경우가 있어 포기했다.

     3-2. Euler phi function을 이용하여 모듈러 역원을 구해보자 했지만 팩토리얼을 계산할 때 수가 0이 되어버리는 경우가 있어 포기했다.

     

    4. 아마 이때 가장 많은 삽질을 한 것 같다. 이항 계수와 관련된 공식과 정리들을 모두 써놓고 새로운 방법이 없는지 많은 고민을 했다. 특히 파스칼 삼각형을 그려놓고 쉽게 구할 수 있는 방법이 없을까 많은 고민을 했다.

     

    5. 1-2시간 정도를 고민하다가 맥주를 마신 덕분인지 조합 0의 개수라는 문제가 떠올랐다. 그래서 nCr의 결과를 소인수분해 해보기로 마음먹었다. 이 방법을 통해 맞았습니다를 받았다.

     

    소스코드가 포함된 풀이는 아래에서 볼 수 있다.

    ...더보기

    먼저 소인수분해를 하기 위해 N이하의 소수를 에라토스테네스의 체를 이용하여 구했다.

    bool sieve[4040404] = { 1, 1 };
    for (int i = 2; i * i <= n; ++i) {
    		if (sieve[i]) continue;
    		for (int j = i * i; j <= n; j += i)
    			sieve[j] = 1;
    	}
    vector<ll> p;
    for (int i = 2; i <= n; ++i)
    	if (!sieve[i]) p.push_back(i);

     

    그리고 해당 소수가 nCr에 몇 개나 들어있는지 세어주기 위한 배열을 만들고

    vector<ll> cnt(p.size());

     

    n!에 들어있는 해당 소수의 개수를 세어주며, r!, (n-r)!에 들어있는 해당 소수의 개수만큼을 빼주는 작업을 했다.

    for (int i = 0; i < p.size(); ++i)
    	for (ll j = p[i]; j <= n; j *= p[i])
    		cnt[i] += ((n / j) - (r / j) - ((n - r) / j));

     

    이후에는 모듈러 연산자의 성질소인수분해 결과를 이용하여 답을 구했다.

    ll res = 1;
    for (int i = 0; i < cnt.size(); ++i) {
    	for (int j = 0; j < cnt[i]; ++j) {
    		res *= p[i];
    		res %= m;
    		if (res == 0) {
    			printf("0"); return 0;
    		}
    	}
    }


    전체 코드는 다음과 같다.

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    bool sieve[4040404] = { 1, 1 };
    int main() {
    	ll n, r, m; scanf("%lld %lld %lld", &n, &r, &m);
    	for (int i = 2; i * i <= n; ++i) {
    		if (sieve[i]) continue;
    		for (int j = i * i; j <= n; j += i)
    			sieve[j] = 1;
    	}
    	vector<ll> p;
    	for (int i = 2; i <= n; ++i)
    		if (!sieve[i]) p.push_back(i);
    	vector<ll> cnt(p.size());
    	for (int i = 0; i < p.size(); ++i)
    		for (ll j = p[i]; j <= n; j *= p[i])
    			cnt[i] += ((n / j) - (r / j) - ((n - r) / j));
        
    	ll res = 1;
    	for (int i = 0; i < cnt.size(); ++i) {
    		for (int j = 0; j < cnt[i]; ++j) {
    			res *= p[i];
    			res %= m;
    			if (res == 0) {
    				printf("0"); return 0;
    			}
    		}
    	}
    	printf("%lld", res);
    }

     

    '공부(Archive) > BOJ' 카테고리의 다른 글

    BOJ 3025: 돌 던지기 (미해결)  (0) 2020.05.13
    BOJ 1509: 팰린드롬 분할  (0) 2020.05.06
    BOJ 3079: 입국심사  (0) 2020.05.06
    BOJ 10775: 공항  (0) 2020.05.06
    BOJ 2613: 숫자구슬  (0) 2020.02.20

    댓글

Designed by Tistory.