洛谷2257 YY的GCD

题目
这是蒟蒻第一道莫比乌斯反演题。
如果这个题要暴力的话应先枚举每一对(i,j)(1<=i<=n,1<=j<=m)判断gcd(i,j)是否为质数,若是质数则答案++;


进行莫比乌斯反演的套路:
换枚举对象,枚举质数p,
洛谷2257 YY的GCD_第1张图片
调整枚举顺序,将p调到前面,那么若使gcd(i,j)=p,则i=ap,j=bp,gcd(a,b)=1;
所以:∑(p为质数)∑(ap<=n)∑(bp<=m)[gcd(a,b)=1];
我们了解在莫比乌斯函数中∑(d|n)μ(d)=1当且仅当n=1时,所以我们可以用这个来代替[gcd(a,b)=1];
所以:∑(p为质数)∑(ap<=n)∑(bp<=m)∑(d|gcd(a,b))μ(d)=1;
继续调整枚举顺序,将 d调到前面,那么a,b应该以d的倍数枚举。
所以:∑(p为质数,p)∑d(d<=min(n,m))μ(d)∑(adp<=n)∑(bdp<=m)1;
化简一下得:∑(p为质数,p)∑d(d<=min(n,m))μ(d)(n/pd)(m/pd),
μ(x),x/pd我们可以事先处理出来。

#include
#include
#include
using namespace std;
typedef long long ll;
ll mu[10000010];
int flag[10000010],prime[10000010],cnt,f[10000010],sum[10000010];
void sieve()
{
	mu[1]=1;
	for(int i=2;i<=10000000;i++)
	{
		if(!flag[i]) prime[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*prime[j]<=10000000;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=cnt;i++)
	 for(int j=1;prime[i]*j<=10000000;j++)
	  f[j*prime[i]]+=mu[j];
	for(int i=1;i<=10000000;i++)
	 sum[i]=sum[i-1]+f[i];
}
ll solve(int a,int b)
{
	ll ans=0;
	if(a>b) swap(a,b);
	for(int l=1,r=0;l<=a;l=r+1)
	{
		r=min(a/(a/l),b/(b/l));
		ans+=(ll)(sum[r]-sum[l-1])*(ll)(a/l)*(ll)(b/l);
	}
	return ans;
}
int main()
{
	sieve();
	int n,m,T;
	scanf("%d",&T);
	while(T)
	{
		T--;
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		printf("%lld\n",solve(n,m));
	}
	return 0;
}

你可能感兴趣的:(洛谷2257 YY的GCD)