SP5971 LCMSUM - LCM Sum 莫比乌斯反演

题意:

戳这里

分析:

挺经典的用 \(\varphi\) 反演的题

\[\displaystyle \sum_{i=1}^nlcm(i,n)\\ =\sum_{i=1}^n\frac{in}{gcd(i,n)}\\ =\sum_{i=1}^n\sum_d\frac{in}{d\times[gcd(i,n)==d]}\\ =\sum_{i=1}^{\lfloor{\frac{n}{d}}\rfloor}\sum_d\frac{idn}{d\times[gcd(i,n)==1]}\\ =n\sum_{d|n}\sum_{i=1}^{\lfloor{\frac{n}{d}}\rfloor}i[gcd(i,\frac{n}{d})==1]\\ =n\sum_{d|n}\sum_{i=1}^d i[gcd(i,d)==1]\\ \]

其中 \(\displaystyle \sum_{i=1}^d i[gcd(i,d)==1]\) 等价于 \(\frac{d*\varphi(d)}{2}\)

证明:

\(\forall \ gcd(i,n)==1\)

存在 \(gcd(n-i,n)==1\)

所以必定存在两个与 \(n\) 互质的数和值为 \(n\)

QED.

总复杂度 \(O(n\log )\) 预处理时需要调和级数,但是要特判 \(d=1\) 时答案为 $1 $


另解:

\[\displaystyle \sum_{i=1}^n\frac{in}{gcd(i,n)}\\ =\frac{1}{2}(\sum_{i=1}^{n-1}\frac{in}{gcd(i,n)}+\sum_{i=n-1}^1\frac{in}{gcd(i,n)})+n\\ =\frac{1}{2}\sum_{i=1}^{n-1}\frac{n^2}{gcd(i,n)}+n\\ =\frac{1}{2}\sum_{d|n}\frac{n^2}{d}\times\varphi(\lfloor\frac{n}{d}\rfloor)+n \]

最后一步枚举 \(d\) 时乘的 \(\varphi(\lfloor{\frac{n}{d}}\rfloor)\) 相当于是 \(gcd(i,n)==d\) 的个数

证明:

\(gcd(i,n)==d\to gcd(\frac{i}{d},\frac{n}{d})==1\) 等价于 \(\varphi\)

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
	typedef long long ll;
	inline ll read()
	{
		ll x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn =  1e6+5;
	ll n,t,cnt;
	ll p[maxn],phi[maxn],f[maxn];
	bool vis[maxn];
	
	void init()
	{
		phi[1]=1;
		for(int i=2;i<=1000000;i++)
		{
			if(!vis[i])
			{
				p[++cnt]=i;
				phi[i]=i-1;
			}
			for(int j=1;j<=cnt&&i*p[j]<=1000000;j++)
			{
				vis[i*p[j]]=true;
				if(i%p[j]==0) 
				{
					phi[i*p[j]]=phi[i]*p[j];
					break;
				}
				phi[i*p[j]]=phi[i]*(p[j]-1);
			}
		}
		for(int i=1;i<=1000000;i++) for(int j=i;j<=1000000;j+=i) f[j]+=phi[i]*i/2;
		for(int i=1;i<=1000000;i++) f[i]=i*f[i]+i;
	}
	
	void work()
	{
		ll a;
		init();
		t=read();
		while(t--)
		{
			a=read();
			printf("%lld\n",f[a]);
		}
	}

}

int main()
{
	zzc::work();
	return 0;
}
posted @ 2021-01-18 11:22  youth518  阅读(76)  评论(0编辑  收藏  举报