洛谷3911:最小公倍数之和

洛谷3911:最小公倍数之和

题意:

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

数据范围\(:n,a_i\leq 50000\)

思路:

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

那么有:
\[ \sum_{i=1}^N\sum_{j=1}^Nc_i\times c_j\times lcm(i,j) \]
\(lcm\)\(gcd\)
\[ \sum_{i=1}^N\sum_{j=1}^Nc_i\times c_j\times \frac{i\times j}{gcd(i,j)} \]
枚举\(gcd(i,j)=k\)
\[ \sum_{i=1}^N\sum_{j=1}^Nc_i\times c_j\times \frac{i\times j}{k}[gcd(i,j)=k] \]
枚举\(k\)
\[ \sum_{k=1}^N\sum_{i=1}^N\sum_{j=1}^Nc_i\times c_j\times \frac{i\times j}{k}[gcd(i,j)=k] \]
枚举\(ik,jk\)
\[ \sum_{k=1}^N\sum_{i=1}^\frac{N}{k}\sum_{j=1}^\frac{N}{k}[gcd(i,j)=1]c_{ik}\times c_{jk}\times i\times j\times k \]
反演:
\[ \sum_{k=1}^N\sum_{i=1}^\frac{N}{k}\sum_{j=1}^\frac{N}{k}\sum_{d|gcd(i,j)}\mu(d)\times c_{ik}\times c_{jk}\times i\times j\times k \]
枚举\(id,jd\)
\[ \sum_{k=1}^N\sum_{i=1}^\frac{N}{kd}\sum_{j=1}^\frac{N}{kd}\sum_{d=1}^\frac{N}{k}\mu(d)\times d^2\times c_{ikd}\times c_{jkd}\times i\times j\times k \]
调整枚举顺序:
\[ \sum_{k=1}^N\sum_{d=1}^\frac{N}{k}\mu(d)\times d^2\sum_{i=1}^\frac{N}{kd}\sum_{j=1}^\frac{N}{kd} c_{ikd}\times c_{jkd}\times i\times j\times k \]
\(kd=T\),将\(k\)提前,并枚举\(T\)
\[ \sum_{T=1}^NT\sum_{d|T}\mu(d)\times d\sum_{i=1}^\frac{N}{T}\sum_{j=1}^\frac{N}{T} c_{iT}\times c_{jT}\times i\times j \]
第二个就相当于是两个\(for\)循环,是一个平方和:
\[ \sum_{T=1}^NT\sum_{d|T}\mu(d)\times d\sum_{i=1}^\frac{N}{T}( c_{iT}\times 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;
}

猜你喜欢

转载自www.cnblogs.com/zxytxdy/p/12378610.html