题解 P3911 【最小公倍数之和】
又是我最喜欢的数论题!!
发现100%的数据中A在50000之内,我们就可以用a[i]来记录A==i的数A有多少个。
然后我们就可以推式子了(我的方法有点鬼畜,接下来写一下我刚刚想起的一个简单的方法。)
首先我们设h[i]为是i倍数的A的两两之积,那么我们可以把是i的倍数的A先加到h[i]中最后让h[i]*=h[i]就好了。
然后再设g[i]为gcd==i的两两数之积,那么我们可得:
h[i]=∑g[j] (i|j)
所以我们可以反演得:
g[i]=∑μ[j/i]*h[j] (i|j)
我们反演得到g之和,最后的答案就是∑g[i]/i (1<=i<=N),N是所有A中最大的一个。
(我一开始的方式是用所有两数之积再减去gcd不为1的。。。。其实原理都一样)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define maxn 50005
ll us[maxn],zs[maxn],t;
bool v[maxn];
ll h[maxn],g[maxn],a[maxn],x,n,N,tot=0;
inline void read(ll&x){
x=0; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}
inline void init(){
us[1]=1;
for(int i=2;i<=maxn;i++){
if(!v[i]) zs[++t]=i,us[i]=-1;
for(int j=1,u;j<=t&&(u=zs[j]*i)<=maxn;j++){
v[u]=1;
if(!(i%zs[j])) break;
us[u]=-us[i];
}
}
}
int main(){
init();
read(n);
for(int i=1;i<=n;i++) read(x),a[x]++,N=max(N,x),tot+=x;
tot*=tot;
for(int i=1;i<=N;i++){
for(int j=i;j<=N;j+=i) if(a[j]) h[i]+=j*a[j];
h[i]*=h[i];
}
for(int i=1;i<=N;i++){
for(int j=i;j<=N;j+=i) g[i]+=us[j/i]*h[j];
tot-=g[i]-g[i]/i;
}
cout<<tot<<endl;
return 0;
}