BOJ 23832 서로소 그래프
sorohue가 PS하는 블로그
BOJ 23832 서로소 그래프
문제 링크입니다.
관찰
2부터 N까지의 각 양의 정수 x마다, x 이하의 정수 중 x와 서로소인 것의 개수를 세 모두 더하면 됩니다. 모든 x마다 직접 1부터 x까지 최대공약수를 구해보기에는 시간이 부족합니다.
풀이1
1부터 N까지의 소수를 모두 구해두면 오일러 피 함수를 이용할 수 있습니다.
x 이하의 정수 중 x와 서로소인 것의 개수는, x의 모든 소인수 p에 대해 x에 (p-1)/p를 곱한 값과 같습니다. 소인수끼리는 약수-배수 관계에 별 영향을 못 미치기 때문에, x 이하의 값 중 x의 각 소인수의 배수인 것들을 비율에 따라 제거할 수 있습니다.
풀이2
이쪽이 제 첫 풀인데요. BOJ의 문제 대부분은 제출 가능한 코드 길이의 제한이 524288B입니다.
N = 50000일 때의 답은 10억을 안 넘는 정도입니다. 하나의 N에 대한 답이 커봤자 대강 10글자 정도면 표현 가능하다는 거죠. 그러면 5만 개의 N에 대한 답을 미리 구해서 몽땅 텍스트로 적어도, 그 글자 수가 50만 정도입니다. 아슬아슬하게 BOJ에 제출 가능한 정도의 길이죠.
그래서, 로컬에서 나이브하게 모든 답을 구하는 코드를 짜고 한 5분 방치해 뒀습니다. 그러고 나온 값을 배열에 넣고 입력받은 N에 맞게 출력하기만 하면, 시간 복잡도 $\mathcal{O}(1)$에 문제를 해결할 수 있습니다.
풀이1 - 코드
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
39
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1234567890;
bool isp[50505]={1,1};
vector<int> prime;
void compute_prime(int x){
for(ll i = 2; i <= x; i++){
if(!isp[i]){
prime.push_back(i);
for(ll j = i*i; j <= x; j += i) isp[j] = 1;
}
}
prime.push_back(INF);
}
ll euler_phi(int n){
ll ret = n;
for(auto p : prime){
if(n == 1) return ret;
if(p > n) break;
if(!(n%p)){
ret /= p;
ret *= p-1;
while(!(n%p)) n /= p;
}
}
return ret-1;
}
int main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
ll n; ll ans = 0; cin >> n;
compute_prime(n);
for(int i = 2; i <= n; i++) ans += euler_phi(i);
cout << ans;
}
풀이2 - 코드
1
2
3
4
5
6
7
8
9
10
#include<iostream>
using namespace std;
int main(){
cin.tie(0); cout.tie(0); ios_base::sync_with_stdio(false);
int a[50000]={0, 1, 3, 5, 9, 11, 17, 21, 27, 31, (...중략...), 759924263};
int n;
cin >> n;
cout << a[n-1];
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.