Gcd HYSBZ - 2818 (莫比乌斯反演)

Gcd

 HYSBZ - 2818 

给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的
数对(x,y)有多少对.

Input

一个整数N

Output

如题

Sample Input

4

Sample Output

4

Hint

hint

对于样例(2,2),(2,4),(3,3),(4,2)

1<=N<=10^7

推导:

本题的本质就是

\sum_{d=1}^{N}[d\in P]\sum_{i=1}^{N}\sum_{j=1}^{N}[gcd(i,j)=d]

后面那个式子我们在莫比乌斯反演中已经见过很多了。

f(x)=\sum_{i=1}^{N}\sum_{j=1}^{N}[gcd(i,j)=x]

则有F(x)=\sum_{x|d}f(d),进一步对F(x)进行推导得到:

F(x)=\sum_{i=1}^{N}\sum_{j=1}^{N}[x|gcd(i,j)]=\sum_{i=1}^{N/x}\sum_{j=1}^{N/x}[1|gcd(i,j)]=floor(\frac{N}{x})*floor(\frac{N}{x})

对F(x)反演推回

f(x)=\sum_{x|d}\mu(\frac{d}{x})F(d)=\sum_{k=1}^{N/x}\mu(k)*F(kx)

则最初的式子就变成了

\sum_{d=1}^{N}[d\in P]\sum_{k=1}^{N/x}\mu(k)*F(kx)

复杂度论证:

预处理mu[],F[],check[],以及最终处理ans都是O(1)的复杂度

主要是处理f(x)的复杂度的。

对于f(x)的处理,我们采用这样一段代码:

for(int i=1;i<=n;i++)
	{
		if(check[i])continue;
		for(int j=i;j<=n;j+=i)
		{
			f[i]+=1ll*mu[j/i]*F[j];
		}
	}

其中的i就是x,j就是kx,可以看出来这个式子的复杂度是

N*(1+\frac{1}{2}+\frac{1}{3}+.....+\frac{1}{N-1}+\frac{1}{N})

这个式子求和较难,不如将它放大一点,成:

N*\int_{1}^{N}\frac{1}{x}dx=N*ln(N)

这个复杂度没有问题

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=1e7;
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
void Moblus()
{
    memset(check,false,sizeof(check));
    mu[1] = 1;
    check[1]=true;
    int tot = 0;
    for(int i = 2; i <= MAXN; i++)
    {
        if( !check[i] ){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot; j++)
        {
            if(i * prime[j] > MAXN) break;
            check[i * prime[j]] = true;
            if( i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }else{
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
}
long long F[MAXN+10],f[MAXN+10];
int main()
{
	Moblus();
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) F[i]=1ll*(n/i)*(n/i);
	for(int i=1;i<=n;i++)
	{
		if(check[i])continue;
		for(int j=i;j<=n;j+=i)
		{
			f[i]+=1ll*mu[j/i]*F[j];
		}
	}
	long long ans=0;
	for(int i=1;i<=n;i++)if(!check[i]) ans+=1ll*f[i];
	printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/82989534