[BOJ 7938] Mniam mniam

2022. 5. 10. 11:44PS/백준

'냠냠' 이라는 문제이다! 냠냠

$n\times m$ 격자에서 원점으로부터 각 점들로 직선을 긋는데 서로 다른 직선의 개수를 구하는 문제로 바꿔 볼 수 있다.  그럼 겹치는 직선이 뭔지만 알면 문제를 풀 수 있겠다. 기울기가 $b/a$이고 기약분수라고 한다면 $2b/2a,\ 3b/3a...$등 이 겹치는 직선이므로 1개만 세야한다. 이때 관찰할 수 있는 사실은 $b/a$가 기약분수가 아니면 반드시 겹치는 직선이 된다는 사실이다. 그럼 $b/a$가 기약분수가 되는 $a,b$쌍의 개수를 세면 된다. 이것은 다음과 같이 나타난다.

\[\sum_{i=1}^{n} \sum_{j=1}^{m} [\gcd(i,j)=1]\]

이때 Mobius inversion을 적용하면

\[\sum_{d=1}^{\min(n,m)} \mu(d)\lfloor n/d \rfloor \lfloor m/d \rfloor \]

아직 고려하지 않은 것은 기울기가 0인 직선과 무한대인 직선이다. $n,m=0$이면 이런 직선은 없을 것이고 $n+m>0,\ nm=0$인 경우 저런 직선은 1개 있다. 이외의 경우 2개가 있다. 따라서 전부 더해주면 된다.

 

아래는 정답 코드가 있다!

더보기

# 코드 보기

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
40
#include<stdio.h>
#define M 1000000
typedef long long ll;
ll mu[M+1];
int sp[M+1];
int min(int a,int b){
    return a>b?b:a;
}
int main(){
    ll i,j;
    for(i=1;i<=M;i++) mu[i]=1;
    for(i=2;i<=M;i+=2){
        if(sp[i]) continue;
        for(j=i;j<=M;j+=i){
            sp[j]=1;
            if(j%(i*i)==0) mu[j]=0;
            else{
                mu[j]*=-1;
            }
        }
        if(i==2) i=1;
    }
    for(i=2;i<=M;i++){
        mu[i]+=mu[i-1];
    }
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        ll ans=0,mi;
        scanf("%d %d",&n,&m);
        mi=min(n,m);
        for(i=1;i<=mi;i=j+1){
            j=min(n/(n/i),m/(m/i));
            ans+=(n/i)*(m/i)*(mu[j]-mu[i-1]);
        }
        if(n*m==0 && n+m) ans+=1;
        if(n*m) ans+=2;
        printf("%lld\n",ans);
    }
}
cs

# 닫기

 

'PS > 백준' 카테고리의 다른 글

[BOJ 18151] DivModulo  (0) 2022.05.16
[BOJ 13174] 괄호  (0) 2022.05.10
[BOJ 3752] 최대공약수 행렬식  (0) 2022.04.14
[BOJ 20202] Euklid  (2) 2022.04.13
[BOJ 18559] Call It What You Want  (0) 2022.04.05