P3911 最小公倍数之和 题解

CSDN同步

原题链接

前置知识:

本题的弱化版

不难发现,原来的:

\[\sum_{i=1}^n \sum_{j=1}^n \operatorname{lcm}(i,j) \]

变成了:

\[\sum_{i=1}^n \sum_{j=1}^n \operatorname{lcm}(a_i , a_j) \]

一言不合就开始推式子。

考虑用 \(c_i\) 表示 \(i\) 出现的次数,然后:

\[\begin{aligned} & \sum_{i=1}^n \sum_{j=1}^n \operatorname{lcm}(a_i , a_j) \\ =& \sum_{i=1}^n\sum_{j=1}^n \operatorname{lcm}(i,j)\times c_i \times c_j\\ =& \sum_{i=1}^n\sum_{j=1}^n \frac{i \times j\times c_i \times c_j}{\gcd(i,j)} \\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d}\rfloor}[\gcd(i,j)=1]d\times i \times j \times c_{id} \times c_{jd} \\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{k|\gcd(i,j)}\mu(k) \times d\times i \times j \times c_{id} \times c_{jd} \\ =& \sum_{d=1}^n\sum_{k=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{kd}\rfloor}\mu(k)\times d \times i \times j \times k^2 \times c_{idk} \times c_{jdk} \\ =& \sum_{T=1}^{n}T\times(\sum_{i=1}^{\lfloor \frac{n}{T} \rfloor}i\times c_{iT})^2\sum_{k|T}\mu(k)\times k \end{aligned} \]

对于后面的:

\[\sum_{k|T} \mu_k \times k \]

可以用 \(O(n \ln n)\) 的时间完成预处理。

\[n \ln n =\sum_{i=1}^n \lfloor \frac{n}{i} \rfloor \]

然后,对中间那块暴力计算即可。

时间复杂度:\(O(n \ln n)\).

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=5e4+1;
typedef long long ll;

inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}

inline void write(ll x) {
	if(x<0) {putchar('-');write(-x);return;}
	if(x<10) {putchar(char(x%10+'0'));return;}
	write(x/10);putchar(char(x%10+'0'));
}

int n,c[N],mu[N],prime[N];
int cnt=0; bool h[N];
ll s[N],ans=0;

inline void Euler(int n) {
	mu[1]=1; for(register int i=2;i<=n;i++) {
		if(!h[i]) mu[i]=-1,prime[++cnt]=i;
		for(register int j=1;j<=cnt && i*prime[j]<=n;j++) {
			h[i*prime[j]]=1;
			if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
			mu[i*prime[j]]-=mu[i];
		}
	} 
} 

int main() {
	n=read(); int maxi=0;
	for(register int i=1,t;i<=n;i++) {t=read(); c[t]++; maxi=max(maxi,t);}
	Euler(maxi); for(register int i=1;i<=maxi;i++)
	for(register int j=i;j<=maxi;j+=i) s[j]+=1ll*mu[i]*i; //预处理
	for(register int T=1;T<=maxi;T++) {
		ll sum=0;
		for(register int i=1;i<=maxi/T;i++) sum+=1ll*c[i*T]*i;
		ans+=T*sum*sum*s[T]; //暴力计算
	} write(ans);
	return 0;
}


posted @ 2020-05-05 17:40  bifanwen  阅读(181)  评论(0编辑  收藏  举报