版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/88849090
Description
链接&多组数据版链接
求
p∈prime∑i=1∑nj=1∑n[gcd(i,j)=p]
n≤107
本题有两种解法,其一为欧拉函数,另一个为莫比乌斯反演,由于本人实力太弱,暂时只会欧拉的方法,当本人学会莫比乌斯反演的时候再补上,见谅!
——2019/3/27
Solution 1
用
gcd的套路日常相除
p∈prime∑i=1∑⌊pn⌋j=1∑⌊pn⌋[gcd(i,j)=1]
由于
gcd(i,j)=gcd(j,i),所以我们可以直接设
i≥j,然后乘上一个2,但是这样会多算一次
i=j=1的情况,所以要对方案数-1
p∈prime∑(i=1∑⌊pn⌋j=1∑i2[gcd(i,j)=1]−1)
最后一个求和符号实际上就是
φ(i),于是乎
p∈prime∑(i=1∑⌊pn⌋2φ(i)−1)
这个时候我们直接用欧拉筛筛出素数,套进公式一看,发现复杂度是
O(素数的个数×n),好想会
T,咋办呢?
注意到第二个求和符号实际上是一个前缀和,所以我们设
sum[i]=∑j=1iφ(i),就可以愉快
O(n)啦
p∈prime∑(sum[⌊pn⌋]−1)
Code 1
#include<cstdio>
using namespace std;
const int N=1e7;
int n,phi[N+1],v[N+1],prime[N+1],m;
long long sum[N+1],ans;
signed main()
{
scanf("%d",&n);
phi[1]=1;sum[1]=1;
for(register int i=2;i<=n;i++)
{
if(!v[i])
{
v[i]=i;prime[++m]=i;
phi[i]=i-1;
}
for(register int j=1;j<=m;j++)
{
if(prime[j]>v[i]||prime[j]*i>n) break;
v[i*prime[j]]=prime[j];
phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
}
}
for(register int i=2;i<=n;i++) sum[i]=sum[i-1]+phi[i];
for(register int i=1;i<=m;i++)
{
if(prime[i]>n) break;
ans+=(sum[n/prime[i]]*2)-1;
}
printf("%lld",ans);
}