【题型探究】公约数之和

整理的算法模板合集: ACM模板


母题:UVA11417 GCD

在这里插入图片描述
数据很小,我们只需要直接暴力枚举即可。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>

using namespace std;
typedef long long ll;
const int N = 5000007;

int gcd(int a, int b)
{
    if(b == 0) return a;
    return gcd(b, a % b);
}

ll solve(int n)
{
    ll res = 0;
    for(int i = 1; i <= n; ++ i){
        for(int j = i + 1; j <= n; ++ j){
            res += gcd(i, j);
        }
    }
    return res;
}
int n;
int main()
{
    while(scanf("%d", &n) != EOF && n){
        printf("%d\n", solve(n));
    }
    return 0;
}

拓展一、UVA11426 GCD - Extreme (II)

在这里插入图片描述
本题的数据开到了 4 ∗ 1 0 6 4*10^{6} 4106,所以暴力一定TLE。

啊,这,范围太大了,我可以莫比乌斯反演 + 整除分块, O ( n ) O(\sqrt{n}) O(n ) 解决!

《算法竞赛入门经典训练指南》上提供了一个构造函数的方法:

我们设 f ( n ) = g c d ( 1 , n ) + g c d ( 2 , n ) + g c d ( 3 , n ) + . . . + g c d ( n − 1 , n ) f(n)=gcd(1,n)+gcd(2,n)+gcd(3,n)+...+gcd(n−1,n) f(n)=gcd(1,n)+gcd(2,n)+gcd(3,n)+...+gcd(n1,n)

答案很明显就是 S ( n ) = f ( 2 ) + f ( 3 ) + . . . + f ( n ) S(n) = f(2) + f(3) + ... + f(n) S(n)=f(2)+f(3)+...+f(n)

我们首先考虑如何求 f ( n ) f(n) f(n)

我们可以先把 g c d ( x , n ) gcd(x, n) gcd(x,n)的值分类,我们发现 g c d ( x , n ) gcd(x,n) gcd(x,n)肯定是n的约数,再设 g ( n , x ) g(n, x) g(n,x)表示 g c d ( x , n ) = i gcd(x,n)=i gcd(x,n)=i的小于 n n n的正整数的个数。

我们发现 [ g c d ( x , n ) = i ] − > [ g c d ( x / i , n / i ) = = 1 ] [gcd(x, n) = i] -> [gcd(x/i, n/i)== 1] [gcd(x,n)=i]>[gcd(x/i,n/i)==1], 和 n / i n/i n/i互质的数的个数即为 φ ( n / i ) φ(n/i) φ(n/i)个。

显然 f ( n ) = ∑ i ∣ n i ∗ g ( n , i ) f(n) = \sum _{i|n} i * g(n, i) f(n)=inig(n,i)

我们直接求一下 S ( n ) S(n) S(n),既是答案。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>

using namespace std;
typedef long long ll;
const int N = 5000007;

ll s[N], primes[N];
ll f[N];
ll n, m, cnt;
ll phi[N];
bool vis[N];

void get_phi(ll n)
{
    vis[1] = true;
    for(int i = 2; i < n; ++ i){
        if(vis[i] == 0){
            primes[ ++ cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && primes[j] * i < n; ++j){
            vis[i * primes[j]] = true;
            if(i % primes[j] == 0){
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            else phi[i * primes[j]] = phi[i] * (primes[j] - 1);
        }
    }
}

int main()
{
    get_phi(N - 5);
    for(int i = 1; i <= N - 1; ++ i){
        for(int j = i * 2; j < N; j += i)
            f[j] += i * phi[j / i];
    }
    for(int i = 1; i <= N - 1; ++ i)
        s[i] = s[i - 1] + f[i];
    while(scanf("%lld\n", &n) == 1 && n){
        printf("%lld\n", s[n]);
    }
    return 0;
}

扩展二、luogu P2398 GCD SUM

在这里插入图片描述

这道题要求的式子是: ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^{n}{\rm gcd}(i,j) i=1nj=1ngcd(i,j)

其实就是: ( ∑ i = 1 n ∑ j = 1 n g c d ( i , j ) [ i < j ] ) × 2 + ∑ i = 1 n i \left(\sum_{i=1}^{n}\sum_{j=1}^{n}{\rm gcd}(i,j)[i<j]\right)\times 2 +\sum_{i=1}^{n}i (i=1nj=1ngcd(i,j)[i<j])×2+i=1ni 答案就应该是:

S ( n ) ∗ 2 + ∑ i = 1 n i S(n) * 2 + \sum_{i = 1}^{n}i S(n)2+i=1ni

S ( n ) S(n) S(n)是上一题的函数。
在这里插入图片描述

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>

using namespace std;
typedef long long ll;
const int N = 100007;

ll s[N], primes[N];
ll f[N];
ll n, m, cnt;
ll phi[N];
bool vis[N];

void get_phi(ll n)
{
    vis[1] = true;
    for(int i = 2; i < n; ++ i){
        if(vis[i] == 0){
            primes[ ++ cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && primes[j] * i < n; ++j){
            vis[i * primes[j]] = true;
            if(i % primes[j] == 0){
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            else phi[i * primes[j]] = phi[i] * (primes[j] - 1);
        }
    }
}

int main()
{
    get_phi(N - 5);
    for(int i = 1; i <= N - 1; ++ i){
        for(int j = i * 2; j < N; j += i)
            f[j] += i * phi[j / i];
    }
    for(int i = 1; i <= N - 1; ++ i)
        s[i] = s[i - 1] + f[i];
    cin >> n;
    ll ans = 0;
    for(int i =1; i <= n; ++ i)
        ans += i;
    ans += 2 * s[n];
    printf("%lld\n", ans);
    return 0;
}

扩展三、luogu P2568 GCD

在这里插入图片描述

这道题要求的式子是: ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) i s   p r i m e ] \sum_{i=1}^{n}\sum_{j=1}^{n}[{\rm gcd}(i,j)\tt is\ prime] i=1nj=1n[gcd(i,j)is prime]

其实就是: ( ∑ i = 1 n s u m p h i ( n i ) [ i   i s   p r i m e ] ) × 2 − ∑ i = 1 n [ i   i s   p r i m e ] \left(\sum_{i=1}^{n}{\rm sumphi}(\frac {n}{i})[i\ \tt is\ prime]\right)\times 2-\sum_{i=1}^{n}[i\ \tt is\ prime] (i=1nsumphi(in)[i is prime])×2i=1n[i is prime]

我们只需要用线性筛筛一下素数即可。
在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;
typedef long long ll;
const int N = 10000007;

int n, m;
int primes[N];
int phi[N];
int cnt;
bool vis[N];
ll sum[N];

void init(int n)
{
    phi[1] = 0;//这里应该是0
    for(int i = 2; i <= n; ++ i){
        if(vis[i] == 0){
            primes[ ++ cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && primes[j] * i <= n; ++ j){
            vis[i * primes[j]] = true;
            if(i % primes[j] == 0){
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            phi[i * primes[j]] = phi[i] * (primes[j] - 1);
        }
    }

    for(int i = 1; i <= n; ++ i)//求phi[n]的前缀和
        sum[i] = sum[i - 1] + phi[i];
}

int main()
{
    scanf("%d", &n);
    init(n);

    ll ans = 0;
    for(int i = 1; i <= cnt; ++ i){
        int p = primes[i];
        ans += sum[n / p] * 2 + 1;
    }
    printf("%lld\n", ans);
    return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页