일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- TTS
- SGNS
- Collaborative Filtering
- Noise Contrastive Estimation
- NEG
- word2vec
- Negative Sampling
- Skip-gram
- 부스트캠프 AI Tech
- CV
- wavenet
- Ai
- Neural Collaborative Filtering
- matrix factorization
- CF
- Dilated convolution
- Tacotron
- BOJ
- Recommender System
- ALS
- 백준
- ANNOY
- Implicit feedback
- RecSys
- FastSpeech2
- Tacotron2
- 논문리뷰
- 추천시스템
- FastSpeech
- Item2Vec
- Today
- Total
devmoon
[C++] 백준 11402 : 이항 계수 4 / 뤼카의 정리 본문
1. 문제 출처
https://www.acmicpc.net/problem/11402
11402번: 이항 계수 4
첫째 줄에 \(N\), \(K\)와 \(M\)이 주어진다. (1 ≤ \(N\) ≤ 1018, 0 ≤ \(K\) ≤ \(N\), 2 ≤ \(M\) ≤ 2,000, M은 소수)
www.acmicpc.net
2. 문제 접근
$10^{18}$ 이하의 자연수 $n$과 $n$ 이하의 음이 아닌 정수 $k$가 주어졌을 때, $_nC_k$의 값을 구하는 문제이다. $_nC_k$ 의 값이 매우 커질 수 있으므로, 2000 이하의 임의의 소수 $m$이 주어지고 $_nC_k$의 값을 $m$으로 나눈 나머지를 출력하면 된다.
이 문제를 해결하기 위해서는 뤼카의 정리에 대해서 알아야 한다. 뤼카의 정리는 $_nC_k$ 를 $m$ 으로 나눈 나머지를 구하고자 할 때 사용할 수 있으며 정확한 동작 원리는 다음과 같다.
$$\binom{n}{r} = \prod_{i=1}^{k}\binom{n_{i}}{r_{i}}\mod p$$
1. $_nC_k$ 의 $n$ 과 $k$ 를 각각 $m$ 진수로 표현한다.
2. $m$진수로 표현된 두 수를 각 자리수에 맞추어 조합을 계산하고, 이 계산된 조합들을 모두 곱하여 $m$ 으로 모듈러 연산을 취한다.
수식만 가지고서는 이해하기 힘들기 때문에 백준의 한 예제를 같이 확인해보자.
$$ \binom{100}{45}\mod 7$$
문제에서 주어진 값들은 $n=100, k=45, m=7$ 이다. 먼저 $n$과 \(k\)의 값을 \(m\)진수로 나타내야 한다. 먼저 100을 7진수로 표현하면 202이고, 45를 7진수로 표현하면 63이다.
7진수로 변환한 두 수에 대해서 각 index에 대해서 다시 조합을 한다. 다음과 같이 수식으로 나타낼 수 있다.
$$\binom{100}{45}\mod 7 = \binom{2}{0}\binom{0}{6}\binom{2}{3} \mod 7$$
하지만 $_nC_r$의 연산과정에서, $r$의 값이 $n$보다 커질 수 있는데, 이 경우에 결과값은 0으로 간주한다.
$$\binom{2}{0}\binom{0}{6}\binom{2}{3} \mod 7 = 1\times0\times0 = 0$$
3. 코드
#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
using namespace std;
ll memo[2001][2001]; // memo[i][j] = iCj의 값이 저장
int m;
vector<int> getLuca(ll val, int mod) {
vector<int> ret;
while (val > 0) {
int remainder = val % mod;
ret.push_back(remainder);
val /= mod;
}
return ret;
}
ll comb(int n, int r) {
if (n < r) return 0;
if (n / 2 < r) r = n - r;
ll &ret = memo[n][r];
if (ret != -1) return ret;
if (r == 0) return ret = 1;
else if (r == 1) return ret = n;
return ret = (comb(n - 1, r - 1) + comb(n - 1, r)) % m;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
memset(memo, -1, sizeof(memo));
ll n, k, res = 1;
cin >> n >> k >> m;
vector<int> lucaN, lucaK;
lucaN = getLuca(n, m);
lucaK = getLuca(k, m);
int minIdx = min(lucaN.size(), lucaK.size());
for (int i = 0; i < minIdx; ++i) {
int n = lucaN[i];
int r = lucaK[i];
res = res * comb(n, r);
res %= m;
}
cout << res << "\n";
return 0;
}
4. 참고한 글
https://ko.wikipedia.org/wiki/%EB%A4%BC%EC%B9%B4%EC%9D%98_%EC%A0%95%EB%A6%AC
뤼카의 정리 - 위키백과, 우리 모두의 백과사전
뤼카의 정리(Lucas' theorem, -定理)는 수론과 조합론에서 이용되는 정리로, 프랑스인 수학자 에두아르 뤼카(Édouard Lucas)의 이름이 붙어 있다. 이 정리는 어떤 조합의 수를 소수 p에 대해 법 p 상에서
ko.wikipedia.org
https://bowbowbow.tistory.com/2
Lucas Theorem : 뤼카의 정리
Lucas Theorem : 뤼카 정리는 음이 아닌 정수 n, k 소수 p에 대해 를 구하는 효율적인 계산 방식을 제공하는 정리입니다. 이항계수는 이므로 n과 k가 크면 계산하기가 상당히 부담스럽습니다. 식
bowbowbow.tistory.com
'알고리즘 > Baekjoon Online Judge' 카테고리의 다른 글
[C++] 백준 16566 : 카드 게임 (4) | 2022.09.19 |
---|---|
[C++] 백준 2517 : 달리기 (0) | 2021.10.16 |
[C++] 백준 10999 : 구간 합 구하기 2 (0) | 2021.10.16 |
[C++] 백준 8111 : 0과 1 (2) | 2021.10.14 |
[C++] 백준 17071 : 숨바꼭질 5 (0) | 2021.10.08 |