Loading [MathJax]/jax/output/CommonHTML/jax.js

[BOJ 13714] 약수의 개수

2022. 2. 8. 17:46PS/백준

위 문제에서 구하고자 하는 것은 다음과 같다.

ai=1bj=1ck=1τ(ijk)

필요한 이론을 증명하고 문제를 해결하자.

 

Theorm 1. τ(ij)=x|iy|j[xy]

 

Lemma 1.

n=ki=0paiit  (pit)

일 때 다음은 n의 약수 중 pi들의 배수의 개수이다.

ki=0aiτ(t)

 

i=pas(ps), j=pbt(pt)라 가정하자. 

τ(ij)=(a+b+1)τ(st)

s,t가 서로소가 아니므로 위 과정을 반복해주자.

τ(ij)=(a0+b0+1)(ai+bi+1)τ(s1t1)=ki=0(ai+bi+1)τ(sk)τ(tk)

gcd(sk,tk)=1이다. 마찬가지 방법으로 τ(i),τ(j)를 전개하자.

τ(i)=ki=0(ai+1) τ(sk)

τ(j)=ki=0(bi+1) τ(tk)

τ(i)τ(j)=ki=0(aibi+ai+bi+1) τ(sk)τ(tk)

τ(ij)τ(i)τ(j)에서 aibi라는 항이 빠진 형태이다. 이것은 i,j의 약수를 각각 x,y라 했을 때 x,y를 동시에 나누는 소수 p가 존재하는 x,y 쌍의 개수를 약수의 곱에서 제외시켜야 함을 의미한다. 즉, 다음과 같이 쓸 수 있다.

 

보충 설명(클릭하세요)

aiτ(sk)biτ(tk)이고 이 항이 사라진 것이므로 Lemma 1에 의해 동시에 pi으로 나누어 떨어지는 약수가 제외됨을 확인할 수 있다. 

 

x|iy|j1x|iy|j[gcd(x,y)>1]

x|iy|j[gcd(x,y)=1]=x|iy|j[xy]

 

위 과정은 i,j 2개인 경우를 진행했다. 3개인 경우는 위의 경우에서 i 또는 j를 더 쪼개면 된다.

τ(ijk)=x|iy|jz|k[xy][yz][zx]

 

 가장 처음의 식을 다음과 같이 변형한다.

ai=1bj=1ck=1x|iy|jz|k[xy][yz][zx]

순서를 바꾼다.

ax=1by=1cz=1[a/x][b/y][c/z][xy][yz][zx]

ax=1[a/x]by=1[b/y][xy]cz=1[c/z][xz]d|y,zμ(d)

y=dp,z=dq라 두자.

ax=1[a/x]min(b,c)d=1μ(d)[xd][b/d]p=1[b/dp][xp][c/d]q=1[c/dq][xq]

W(s,t)=si=1[s/i][ti]

라 정의한다. 다시 표현하면

ax=1[a/x]min(b,c)d=1μ(d)[xd]W([b/d],x)W([c/d],x)

[xy]의 누적합을 전처리 한다면 WO(s)안에 계산할 수 있다. μ(y)[xy]의 누적합을 전처리 한다면 min(b,c)d=1O(n)으로 계산할 수 있다. 따라서 전체 시간 복잡도는 O(n2)이다.

 

이것을 구현한 코드이다.

코드 보기

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<stdio.h>
typedef long long ll;
#define sz 2001
ll cop[sz][sz];
ll pre_cop[sz][sz];
ll mu_cop[sz][sz];
ll mu[sz];
ll mod=1<<30;
ll sp[sz];
ll prime[sz],pt;
ll pre_bc[sz];
ll min(ll a,ll b){
    return a>b?b:a;
}
ll W(ll s,ll t){
    ll i,j,sum=0;
    for(i=1;i<=s;i=j+1){
        j=s/(s/i);
        sum+=s/i*(pre_cop[t][j]-pre_cop[t][i-1])%mod;
        sum%=mod;
    }
    return sum;
}
int main(){
    ll i,j,k;
    for(i=1;i<sz;i++){
        for(j=1;j<sz;j++) cop[i][j]=1;
    }
    for(i=2;i<sz;i++){
        ll h=i;
        for(j=2;j*j<=h;j++){
            if(h%j==0){
                for(k=j;k<sz;k+=j){
                    cop[i][k]=0;
                }
                while(h%j==0) h/=j;
            }
        }
        if(h-1){
            for(j=h;j<sz;j+=h) cop[i][j]=0;
        }
        for(j=1;j<sz;j++) pre_cop[i][j]=cop[i][j]+pre_cop[i][j-1];
    }
    for(i=1;i<sz;i++){
        pre_cop[1][i]=cop[1][i]+pre_cop[1][i-1];
    }
    mu[1]=1;
    for(i=2;i<sz;i++){
        if(!sp[i]){
            prime[pt++]=i;
            mu[i]=-1;
        }
        for(j=0;j<pt;j++){
            if(i*prime[j]>sz) break;
            sp[i*prime[j]]=1;
            if(i%prime[j]==0break;
            mu[i*prime[j]]=mu[i]*mu[prime[j]];
        }
    }
    for(i=1;i<sz;i++){
        for(j=1;j<sz;j++){
            mu_cop[i][j]=mu[j]*cop[i][j]+mu_cop[i][j-1];
        }
    }
    ll a,b,c,sum=0;
    scanf("%lld %lld %lld",&a,&b,&c);
    for(i=1;i<=min(b,c);i=pre_bc[i]+1){
        pre_bc[i]=min(b/(b/i),c/(c/i));
    }
    for(i=1;i<=a;i++){
        ll s=0;
        for(j=1;j<=min(b,c);j=k+1){
            k=pre_bc[j];
            s+=W(b/j,i)*W(c/j,i)%mod*(mu_cop[i][k]-mu_cop[i][j-1])%mod;
            s%=mod;
        }
        sum=(sum+s*(a/i)%mod)%mod;
    }
    printf("%lld",sum);
}
cs

 

 

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

[BOJ 17105] 골드바흐 트리플  (0) 2022.03.04
[BOJ 16808] Identity Function  (0) 2022.02.14
[BOJ 18496] Euclid's Algorithm  (0) 2022.01.16
64일 스트릭!!  (0) 2021.12.20
[BOJ 10438] 페리 수열의 합  (0) 2021.11.20