题意:
给定整数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,
看了一篇博客:http://blog.csdn.net/u014610830/article/details/49390831
里面讲的很详细,
设
sum(d)=∑p|dμ(dp)
这里比较特殊的是
p
是质数。在素数筛法中
sum(d∗p)
这个值如果快速更新?
当
p|d
时:
sum(d∗p)=μ(d)
。
当
p
不能整除
d
时:
sum(d∗p)=μ(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;
}