洛谷3911:最小公倍数之和

洛谷3911:最小公倍数之和

题意:

给定(n)个数,求(sumsum lcm(a_i,a_j))

数据范围(:n,a_ileq 50000)

思路:

(a_i)很小所以我们用(c)数组来记录一下每个数字出现的次数,设(N)表示最大的(a_i)

那么有:

[sum_{i=1}^Nsum_{j=1}^Nc_i imes c_j imes lcm(i,j) ]

(lcm)(gcd)

[sum_{i=1}^Nsum_{j=1}^Nc_i imes c_j imes frac{i imes j}{gcd(i,j)} ]

枚举(gcd(i,j)=k)

[sum_{i=1}^Nsum_{j=1}^Nc_i imes c_j imes frac{i imes j}{k}[gcd(i,j)=k] ]

枚举(k)

[sum_{k=1}^Nsum_{i=1}^Nsum_{j=1}^Nc_i imes c_j imes frac{i imes j}{k}[gcd(i,j)=k] ]

枚举(ik,jk)

[sum_{k=1}^Nsum_{i=1}^frac{N}{k}sum_{j=1}^frac{N}{k}[gcd(i,j)=1]c_{ik} imes c_{jk} imes i imes j imes k ]

反演:

[sum_{k=1}^Nsum_{i=1}^frac{N}{k}sum_{j=1}^frac{N}{k}sum_{d|gcd(i,j)}mu(d) imes c_{ik} imes c_{jk} imes i imes j imes k ]

枚举(id,jd)

[sum_{k=1}^Nsum_{i=1}^frac{N}{kd}sum_{j=1}^frac{N}{kd}sum_{d=1}^frac{N}{k}mu(d) imes d^2 imes c_{ikd} imes c_{jkd} imes i imes j imes k ]

调整枚举顺序:

[sum_{k=1}^Nsum_{d=1}^frac{N}{k}mu(d) imes d^2sum_{i=1}^frac{N}{kd}sum_{j=1}^frac{N}{kd} c_{ikd} imes c_{jkd} imes i imes j imes k ]

(kd=T),将(k)提前,并枚举(T)

[sum_{T=1}^NTsum_{d|T}mu(d) imes dsum_{i=1}^frac{N}{T}sum_{j=1}^frac{N}{T} c_{iT} imes c_{jT} imes i imes j ]

第二个就相当于是两个(for)循环,是一个平方和:

[sum_{T=1}^NTsum_{d|T}mu(d) imes dsum_{i=1}^frac{N}{T}( c_{iT} imes i)^2 ]

枚举第一个求和,预处理第二个求和,暴力计算第三个求和。

总时间复杂度相当于是(n)乘上(n)项调和级数求和,为(O(nlnn))

#include<bits/stdc++.h>
using namespace std;
const int maxn =  5e4+10;
typedef long long ll;

int primes[maxn], tot;
ll mu[maxn];
ll sum[maxn];
bool vis[maxn];
void init(int n)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!vis[i])
        {
            primes[++tot] = i;
            mu[i] = -1;
        }
        for(int j = 1; primes[j] <= n/i; j++)
        {
            vis[primes[j]*i] = 1;
            if(i % primes[j] == 0) break;
            else mu[i*primes[j]] = -mu[i];
        }
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n/i; j++)
            sum[i*j] += i*mu[i];
}

int n, cnt[maxn], m;

int main()
{
    init((int)5e4);
    scanf("%d", &n);
    for(int i = 1, x; i <= n; i++)
    {
        scanf("%d", &x);
        cnt[x]++; m = max(m, x);
    }
    ll ans = 0;
    for(int T = 1; T <= m; T++)
    {
        ll tmp = 0;
        for(int i = 1; i <= m/T; i++)
            tmp += 1ll*cnt[i*T]*i;
        ans += T*sum[T]*tmp*tmp;
    }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/zxytxdy/p/12378610.html