https://www.acmicpc.net/problem/14860
14860번: GCD 곱
N과 M 이 주어졌을 때, GCD(1, 1) × GCD(1, 2) × ... × GCD(1, M) × GCD(2, 1) × GCD(2, 2) × ... × GCD(2, M) × ... × GCD(N, 1) × GCD(N, 2) × ... × GCD(N, M)을 구하는 프로그램을 작성하시오.
www.acmicpc.net
$GCD\left ( 1,1 \right)\times \cdots \times GCD\left ( N,M \right)$ 을 구하는 문제입니다.
$N\;,\;M$ 의 범위를 살펴보니, 위 식을 소인수분해하는 게 가능할 것 같습니다.
그러면 eratosthenes' sieve로 15000000 이하의 소수를 모두 구하고 나면,
각 소수가 몇 번 등장하는 지가 중요하겠네요.
$N=M=9$ 로 가정하고 $2$ 와 $3$ 의 등장 횟수를 살펴보겠습니다.
$\left( 1 \right)$ $2$ 의 등장 횟수
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | 1 | 1 | 1 | 1 | |||||
3 | |||||||||
4 | 1 | 1+1 | 1 | 1+1 | |||||
5 | |||||||||
6 | 1 | 1 | 1 | 1 | |||||
7 | |||||||||
8 | 1 | 1+1 | 1 | 1+1+1 | |||||
9 |
첫번째 단계로, $GCD\left ( i,j \right)$ 에 2가 포함되는 곳에 '1' 을 씁니다.
두번째는, $GCD\left ( i,j \right)$ 에 4가 포함되는 곳에 "+1" 을 씁니다.
이미 첫번째 단계에서 2를 한 번 세고 지나간 곳이기 때문입니다.
세번째는, $GCD\left ( i,j \right)$ 에 8이 포함되는 곳에 "+1" 을 씁니다.
이미 첫번째, 두번째 단계에서 2를 두 번 세고 지나갔기 때문입니다.
더 이상의 단계를 진행해도 9까지의 숫자만 있는 위 표에서는 의미가 없습니다.
그렇다면, 2가 등장한 횟수는, 다음과 같겠군요.
$2$ 의 배수의 갯수 $+$ $2^{2}$ 의 배수의 갯수 $+$ $2^{3}$ 의 배수의 갯수
$\left( 2 \right)$ $3$ 의 등장 횟수
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | 1 | 1 | 1 | ||||||
4 | |||||||||
5 | |||||||||
6 | 1 | 1 | 1 | ||||||
7 | |||||||||
8 | |||||||||
9 | 1 | 1 | 1+1 |
첫번째 단계로, $GCD\left ( i,j \right)$ 에 3이 포함되는 곳에 '1' 을 씁니다.
두번째는, $GCD\left ( i,j \right)$ 에 9가 포함되는 곳에 "+1" 을 씁니다.
이미 첫번째 단계에서 3을 한 번 세고 지나간 곳이기 때문입니다.
더 이상의 단계를 진행해도 9까지의 숫자만 있는 위 표에서는 의미가 없습니다.
그렇다면, 3이 등장한 횟수는, 다음과 같겠군요.
$3$ 의 배수의 갯수 $+$ $3^{2}$ 의 배수의 갯수
이제 $N\times M$ 테이블로 확장합시다.
소수 $p$ 가 등장하는 횟수는 다음과 같을 것입니다.
$p$ 의 배수의 갯수 $+$ $p^{2}$ 의 배수의 갯수 $+$ $p^{3}$ 의 배수의 갯수 $+\cdots$
그런데 $p^{k}$ 가 등장하는 횟수는 분명히 다음과 같습니다.
$p^{k}$ 의 배수가 $N\times M$ 테이블에서 등장하는 횟수 : $\left \lfloor \frac{N}{p^{k}} \right \rfloor \times \left \lfloor \frac{M}{p^{k}} \right \rfloor$
따라서 우리는 다음을 구합니다.
$\sum_{k=1}\left \lfloor \frac{N}{p^{k}} \right \rfloor \times \left \lfloor \frac{M}{p^{k}} \right \rfloor$
이 값은 $GCD\left( 1,1 \right)\times GCD\left( N,M \right)$ 의 값에서 $p$ 가 곱해지는 횟수입니다.
그 횟수만큼 거듭제곱하는 것은 분할정복으로 해결하면 됩니다.
이제 sieve 를 활용하여 구현한 코드는 다음과 같습니다.
특별히 2의 배수만 먼저 처리한 것은 실행속도가 더 빨라지기 때문입니다.
#include <stdio.h>
#include <stdbool.h>
#define mod 1000000007
bool composite[15000001];
long long power_modular (long long x, long long y);
int main() {
long long N, M;
scanf("%lld %lld", &N, &M);
long long min = (M > N) ? N : M;
long long count = 0;
for (int i = 2; i <= min; i *= 2) {
count += ((N/i) * (M/i));
}
long long answer = power_modular (2, count);
for (long long i = 3; i <= min; i += 2) {
if (!composite[i]) {
count = 0;
for (long long j = i; j <= min; j *= i) {
count += ((N/j) * (M/j));
}
answer = (answer * power_modular(i, count)) % mod;
for (long long j = 3 * i; j <= min; j += 2 * i) {
if (!composite[j]) {
composite[j] = true;
}
}
}
}
printf("%lld", answer % mod);
return 0;
}
long long power_modular (long long x, long long y) {
x %= mod;
long long result = 1;
while (y) {
if (y % 2) {
result = (result * x) % mod;
}
x = (x * x) % mod;
y /= 2;
}
return result;
}
(C11, 15764KB, 128ms, 제출번호 26812591)
2의 배수를 먼저 처리하지 않아도 200ms 에 정답 처리됐습니다. (제출번호 26812126)
'분할 정복을 이용한 거듭제곱 > 정수 거듭제곱' 카테고리의 다른 글
백준 16176번: Liar Game (0) | 2021.12.20 |
---|---|
백준 13182번: 제비 (0) | 2021.08.02 |
백준 13173번: Ω (0) | 2021.07.31 |
백준 12446번: バクテリアの増殖 (Large) (0) | 2021.07.19 |
백준 12445번: バクテリアの増殖 (Small) (0) | 2021.07.19 |