jzoj4496-[GDSOI2016]互补约数【莫比乌斯反演】

正题

题目链接:https://gmoj.net/senior/#main/show/4496


题目大意

给出(n),定义

[f(i)=sum_{d|i}gcd(d,frac{i}{d}) ]

[sum_{i=1}^nf(i) ]

(1leq nleq 10^{11})


解题思路

考虑枚举(x=d)(y=frac{n}{d})

[sum_{x=1}sum_{y=1}[xyleq n]gcd(x,y) ]

[sum_{d=1}dsum_{x=1}sum_{y=1}[xyd^2leq n][gcd(x,y)=1] ]

然后莫反

[sum_{d=1}dsum_{k=1}mu(k)sum_{x=1}sum_{y=1}[xyd^2k^2leq n] ]

[sum_{d=1}dsum_{k=1}mu(k)sum_{x=1}lfloorfrac{lfloorfrac{n}{d^2k^2} floor}{x} floor ]

因为要求(k^2d^2leq n)所以可以考虑暴力枚举(k)(d),然后最后那个整除分块就好了。

这样会慢几秒,把后面那个式子每次算的时候顺便记忆化了就可以了。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const ll N=316228;
ll n,ans,mu[N],pri[N/10],p1[N],p2[N],cnt,S,T;
bool v[N];map<ll,ll> mp;
void Prime(){
	mu[1]=1;
	for(ll i=2;i<N;i++){
		if(!v[i])pri[++cnt]=i,mu[i]=-1;
		for(ll j=1;j<=cnt&&i*pri[j]<N;j++){
			v[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			mu[i*pri[j]]=-mu[i];
		}
	}
}
ll GetF(ll n){
	ll ans=0;
	if(n>=T&&p2[S/n])return p2[S/n];
	if(n<T&&p1[n])return p1[n];
	for(ll l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=(n/l)*(r-l+1); 
	}
	if(n>=T)return p2[S/n]=ans;
	return p1[n]=ans;
}
ll GetG(ll n){
	ll ans=0;
	for(ll i=1;i*i<=n;i++)
		ans+=mu[i]*GetF(n/i/i);
	return ans;
}
signed main()
{
	freopen("gcd.in","r",stdin);
	freopen("gcd.out","w",stdout);
	Prime();
	scanf("%lld",&n);
	T=sqrt(n);S=n;
	for(ll i=1;i*i<=n;i++)
		ans+=i*GetG(n/i/i);
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15006174.html