• 洛谷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;
    }
    
    
  • 相关阅读:
    POJ1422 最小路径覆盖
    POJ1422 最小路径覆盖
    POJ1125 Floyd
    POJ1125 Floyd
    POJ2570 二进制,位运算,Floyd
    POJ2570 二进制,位运算,Floyd
    POJ2446 二分匹配
    POJ2536 二分图匹配
    POJ2536 二分图匹配
    POJ3692 最大点权独立集元素个数
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/12378610.html
Copyright © 2020-2023  润新知