洛谷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;
}