考察内容:Euler函数
题意:求Σ(1<=i,j<=n,gcd(i,j)=p)1
分析:
若gcd(i,j)=p,将i与j同除以p,则gcd(i/p,j/p)=1,而i,j最大值为n,则在p的情况下即为求i/p(i<=n)与多少小于它的数互质,值为phi[1/p]+…+phi[n/p]值,枚举每一个p,将Σphi利用一个前缀和预处理一下,最后减去质数个数即可((p,p)只算一次)。
题意:求Σ(1<=i,j<=n,gcd(i,j)=p)1
分析:
若gcd(i,j)=p,将i与j同除以p,则gcd(i/p,j/p)=1,而i,j最大值为n,则在p的情况下即为求i/p(i<=n)与多少小于它的数互质,值为phi[1/p]+…+phi[n/p]值,枚举每一个p,将Σphi利用一个前缀和预处理一下,最后减去质数个数即可((p,p)只算一次)。
注意开long long不然会炸
Euler函数
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=10000005;
int n,cnt,k;
bool is[maxn];
long long pre[maxn];
int prime[maxn];
int phi[maxn];
void linear_shaker()
{
int t;
phi[1]=1;
for(int i=2;i<maxn;i++)
{
if(!is[i])
{
phi[i]=i-1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&(t=prime[j]*i)<maxn;j++)
{
is[t]=true;
if(i%prime[j]==0)
{
phi[t]=phi[i]*prime[j];
break;
}
phi[t]=phi[i]*phi[prime[j]];
}
}
}
int main()
{
scanf("%d",&n);
long long ans=0;
linear_shaker();
pre[1]=1;
for(int i=2;i<=n;i++)
pre[i]=pre[i-1]+phi[i];
int i;
for(i=1;i<=cnt;i++)
{
ans+=pre[n/prime[i]];
if(prime[i]>n)break;
}
i--;
printf("%lld",(ans<<1)-i);//(a,b)与(b,a)应计算两次
//要注意的是当p为质数时,(p,p)只能计算一次(而默认计算了两次),所以需要减去已扫过的质数个数
}
还有一种做法是利用莫比乌斯反演
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=10000010;
int n,cnt;
int prime[maxn];
bool is[maxn];
int miu[maxn];
long long ans;
void linear_shaker()
{
int tmp;
miu[1]=1;
for(int i=2;i<maxn;i++)
{
if(!is[i])
{
miu[i]=-1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&(tmp=prime[j]*i)<maxn;j++)
{
is[tmp]=true;
if(i%prime[j]==0)
{
miu[tmp]=0;
break;
}
miu[tmp]=-miu[i];
}
}
for(int i=2;i<maxn;i++)
miu[i]+=miu[i-1];
}
long long getans(long long m,int k)
{
long long res=0;
int pos;
m/=k;
for(int i=1;i<=m;i=pos+1)
{
pos=(m/(m/i));
res+=1LL*(miu[pos]-miu[i-1])*(m/i)*(m/i);
}
return res;
}
int main()
{
scanf("%d",&n);
linear_shaker();
for(int i=1;i<=cnt&&prime[i]<=n;i++)ans+=getans(n,prime[i]);
printf("%lld",ans);
return 0;
}