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