题意:1<=x,y<=n,gcd(x,y)==质数的对数
解1:欧拉函数
gcd(x,y)==k
gcd(x/k, y/k)==1
那么题意就变成了在 [ 1, n/k ]之间有多少对( x,y )互质,然后我们用到了欧拉函数。
这里我们用到了一个线性欧拉筛顺便筛出来素数
void getphi() {
phi[1] = 1;
int cnt = 0;
for (int i = 2; i <= N; i++) {
if (!check[i]) {
pri[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt; j++) {
if (i * pri[j] > N) break;
check[i * pri[j]] = 1;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
else phi[i * pri[j]] = phi[i] * (pri[j] - 1);
}
}
for (int i = 1; i <= N; i++)
sum[i] = (sum[i - 1] + phi[i]) % mod;
}
将欧拉函数处理成前缀和,再枚举小于n的质数p将sum[n/p]加和
因为(x,y)(y,x)我们都要算,但是(1,1)这个算了两遍所以 -1
ans=∑(sum [ n/ pri [ i ] ])*2-1 (pri[ i ]<n)
完整代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+5;
int n,tot;
int phi[N],pri[N];
bool mark[N];
ll sum[N];
void getphi()//线性筛欧拉顺便筛质数
{
phi[1]=1;
tot=0;
memset(mark,0,sizeof(mark));
mark[0]=1;
mark[1]=1;
for(int i=2; i<=N; i++)
{
if(!mark[i])
{
phi[i]=i-1;
pri[++tot]=i;
}
for(int j=1; j<=tot&&i*pri[j]<=N; j++)
{
int x=pri[j];
mark[i*x]=1;
if(i%x==0)
{
phi[i*x]=phi[i]*x;
break;
}
else
phi[i*x]=phi[i]*(pri[j]-1);
}
}
}
int main()
{
scanf("%d",&n);
getphi();
for(int i=1; i<=n; i++)
{
sum[i]=sum[i-1]+phi[i];
}
ll ans=0;
for(int i=1; i<=tot; i++)
{
ans+=sum[n/pri[i]]*2-1;
}
printf("%lld\n",ans);
return 0;
}
解2:莫比乌斯反演
我们设f(n)为gcd(i,j)=k的个数,F(n)为gcd(i,j)=k的倍数的个数,k是质数
同样gcd(x,y)=k,gcd(x/k, y/k)=1,我们的范围变成了【1,n/k】
我们要求的就是f(1),代入莫比乌斯反演倍数公式
f(n)=∑n|dμ(d/n)F(d) n=1
f(1)=∑μ(d)F(d) (∑的范围从d=1到d=n/k)
上面的式子两个乘数要向下取整我打不出来惹QAQ
完整代码如下:
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=1e7+10;
int mo[N],prime[N],n,cnt;
bool vis[N];
void mobius()
{
mo[1]=1;
vis[1]=1;
for(int i=2;i<N;i++)
{
if(!vis[i])
{
prime[cnt++]=i;
mo[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<N;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j])
{
mo[i*prime[j]]=-mo[i];
}
else
{
mo[i*prime[j]]=0;
break;
}
}
}
}
int main()
{
mobius();
scanf("%d",&n);
ll ans=0;
for(int i=0;i<cnt&&prime[i]<=n;i++)
{
ll tmp=n/prime[i];
for(int j=1;j<=tmp;j++)
{
ans+=(ll)mo[j]*(tmp/j)*(tmp/j);
}
}
printf("%lld\n",ans);
return 0;
}
这个比较迷用莫比乌斯一定要先预处理再输入,,,