포스트

BOJ 14860 GCD 곱

sorohue가 PS하는 블로그

BOJ 14860 GCD 곱

문제 링크입니다.

더블 카운팅

$\prod _{i=1} ^{N} \prod _{j=1} ^{M} \gcd (i, j)$를 구해야 합니다.

약수에 대한 합이나 곱 문제는 배수로 바꿔서 생각하면 쉬운 경우가 많습니다.

각 g마다 최대공약수가 g인 순서쌍의 개수를 구해 그만큼 거듭제곱해 주는 방식으로 문제를 풀어봅시다.

소인수마다 생각하기

g를 공약수로 갖는 순서쌍의 개수는 쉽게 구할 수 있지만, 최대공약수는 조금 다릅니다. 포함-배제의 원리를 이용해 다량의 계산으로 꾸역꾸역 각각 구하는 방법이 있겠지만, 좀 더 쉽게 풀어 봅시다.

g를 소인수분해하고, 각각의 소인수에 대해 문제를 풉시다. 각각의 소인수는 약수-배수 관계가 없어서 서로의 답에 영향을 주지 않습니다. 예를 들어 소인수 2에 대해 문제를 푸는 경우, 2 ^ (4의 배수가 아닌 2의 배수의 순서쌍 개수), 4 ^ (8의 배수가 아닌 4의 배수의 순서쌍 개수) … 를 구해 모두 곱하면 됩니다.

이것도 세는 방식을 바꿔서, 2 ^ (2의 배수의 순서쌍 개수), 2 ^ (4의 배수의 개수), …를 구해 모두 곱하는 것으로 같은 값을 얻을 수 있습니다.

결국 $\min(N, M)$ 이하의 모든 소수를 찾아두고, 각 소수마다 위의 작업을 반복하는 것으로 문제를 해결할 수 있습니다.

코드

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
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod = 1e9+7;
bool isp[15151515]={1,1};
vector<ll> p;

ll pw(ll n, ll r){
	ll ret = 1;
	while(r){
		if(r&1) ret = ret*n%mod;
		n = n*n%mod;
		r >>= 1;
	}
	return ret;
}

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
	for(ll i = 2; i*i <= 15000000; i++){
		if(!isp[i]){
			for(ll j = i*i; j <= 15000000; j += i) isp[j] = 1;
		}
	}
	for(ll i = 2; i <= 15000000; i++) if(!isp[i]) p.push_back(i);
	ll n, m; cin >> n >> m;
	if(n > m) swap(n, m);
	ll ans = 1;
	for(auto i : p){
		if(i > n) break;
		ll now = i;
		while(now <= n){
			ans = ans*pw(i, (n/now)*(m/now))%mod;
			now *= i;
		}
	}
	cout << ans;
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.