Gcd HYSBZ - 2818

题意:

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

思路:

想想容斥,莫比乌斯,最先想到的还是欧拉函数

k为质数,gcd(a,b)=k,所以有gcd(a/k,b/k)=1;

显然欧拉函数可以轻易得到gcd()=1的一堆答案

有两种方案:

遍历1-n的欧拉函数值每个乘以(n/i)的素数个数前缀和,然后果断超时了。。。。。。

这里的时间复杂度是O(n);

另外一种方案,

遍历1-n的所有素数,每个乘以(n/primer[i])的欧拉函数前缀和,然后过了。。。。。

这样时间复杂度显然降低了不少,甚至随着n的增大,可以达到O(n/In(n))..

由于是数对,这里记得要乘以2的处理,而(1,1)乘以二了显然要减去1

又一次欧拉函数实现了容斥

#include<stdio.h>
#include<string.h>
#define maxn 10000009
int p[maxn];
bool book[maxn];
long long s[maxn];
int prime[maxn];
int num=0; 
void init()
{
	 memset(book,true,sizeof(book));
	 p[1]=1;
	for(int i=2;i<maxn;i++)
	{
		if(book[i])
		{
			prime[num++]=i;
			p[i]=i-1;
		}
	   for(int j=0;j<num && prime[j]*i<=maxn;j++)
        {
           book[i*prime[j]]=false;
            if(i%prime[j]==0)
            {
                p[i*prime[j]]=p[i]*prime[j];
                break;
            }
            else p[i*prime[j]]=p[i]*(prime[j]-1);
        }
	}
	s[0]=0; 
    for(int i=1;i<maxn;i++)
    s[i]=s[i-1]+p[i];
}
int main()
{
	init();
	int n;
	long long a;
	while(~scanf("%d",&n))
	{
	   a=0;
		for(int i=0;n>=prime[i];i++)
			a+=(2*s[n/prime[i]]-1);//减一是为了    欧拉函数(1)=1 
			printf("%lld\n",a); 
		
	}
	return 0;
 } 
莫比乌斯

我们套路的知道

F(d)= sigma(f(i))   [ d|i ]

f(t)  = sigma u(i/d) * F(i)  [d|i]

t遍历全部质数

ans = sigma  (   sigma u(i/d) * F(i)  [d|i]   )  [d为质数]

对于不同的质数d ,   F[i]=(n/ i)*(n/ i)    显然是一样的,u(i/d)是不一样的

比如 F[12]=(n/12)*(n/12) ,对于d=2  与  d=3 时,u(12/3) 与  u(12/2)

也就是说我们可以通过每种莫比乌斯函数和的形式,得到答案

如sum[12]=u[12/2] + u[12/3] 

    sum[30]=u[30/2]+u[30/3]+u[30/5]

于是我们只要O(n)遍历一遍,ans+=( (n/ i)*(n/ i)* sum[i]);

那么sum[i]要怎么求?

当n不等于1时,n所有因子的莫比乌斯函数值的和为0,

sum(i)=sum()

看了一篇博客:http://blog.csdn.net/u014610830/article/details/49390831

里面讲的很详细,

sum(d)=p|dμ(dp)  
这里比较特殊的是 p 是质数。在素数筛法中 sum(dp) 这个值如果快速更新?

p|d 时: sum(dp)=μ(d) 。 
p 不能整除 d 时: sum(dp)=μ(d)sum(d)

o(n)复杂度算出了 sum(x) 值。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000010;
int prime[maxn],mu[maxn],sum[maxn];
bool check[maxn];
void Mobius(){
    memset(check,false,sizeof(check));
    mu[1] = 1;
    prime[0] = 0;
   for(int i=2;i<maxn;i++){
        if(!check[i]){
            mu[i] = -1;
            sum[i] = 1;
            prime[++prime[0]] = i;
        }
        for(int j=1;j<=prime[0];j++){
            if(i*prime[j] >= maxn)  break;
            check[i*prime[j]] = true;
            if(i % prime[j]){
                mu[i*prime[j]] = -mu[i];
                sum[i*prime[j]] = mu[i] - sum[i];
            }
            else{
                mu[i*prime[j]] = 0;
                sum[i*prime[j]] = mu[i];
                break;
            }
        }
    }
}
int main()
{
    Mobius();
    long long ans=0;
    int n;
    while(~scanf("%d",&n)){
        ans=0;
        for(int i=2;i<=n;i++)
        ans += (long long)(n/i)*(long long)(n/i)*sum[i];
        printf("%lld\n",ans);
    }
    return 0;
}














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值