51nod 1594 Gcd and Phi 莫比乌斯反演

本文介绍了一种利用数论反演技巧解决特定数学问题的方法。该问题要求计算从1到n范围内所有整数的欧拉函数值的两两最大公约数的欧拉函数之和。文章详细解释了如何通过枚举、换元、反演等步骤简化原始表达式,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
给出n(n<=2106)n(n<=2∗106),求
ni=1nj=1ϕ(gcd(ϕ(i),ϕ(j)))∑i=1n∑j=1nϕ(gcd(ϕ(i),ϕ(j)))的值。

分析:
看到这种题显然想反演了。

ans=i=1nj=1nϕ(gcd(ϕ(i),ϕ(j)))ans=∑i=1n∑j=1nϕ(gcd(ϕ(i),ϕ(j)))

=D=1nϕ(D)i=1nj=1n[gcd(ϕ(i),ϕ(j))==D]=∑D=1nϕ(D)∑i=1n∑j=1n[gcd(ϕ(i),ϕ(j))==D]

然后我们枚举ϕ(i)ϕ(i)ϕ(j)ϕ(j),我们设ad=ni=1[ϕ(i)==d]ad=∑i=1n[ϕ(i)==d]x=phi(i)x=phi(i)y=phi(j)y=phi(j),其实可以理解为换元,则原式可以变为,
=D=1nϕ(D)x=1ny=1n[gcd(x,y)==D]axay=∑D=1nϕ(D)∑x=1n∑y=1n[gcd(x,y)==D]∗ax∗ay

然后就是很显然的反演了,我们设sumi=i|tatsumi=∑i|tat
=D=1nϕ(D)d=1nμ(d)(sumDd)2=∑D=1nϕ(D)∑d=1nμ(d)∗(sumD∗d)2

T=DdT=Dd,则
=T=1n(sumT)2d|Tμ(d)ϕ(T/d)=∑T=1n(sumT)2∑d|Tμ(d)∗ϕ(T/d)

f(i)=d|iμ(d)ϕ(i/d)f(i)=∑d|iμ(d)∗ϕ(i/d),是个狄利克雷卷积,一定是个积性函数,直接可以上线筛。对于x=pkx=pk,则f(x)=ϕ(x)ϕ(x/p)f(x)=ϕ(x)−ϕ(x/p)。这个sumsum可以枚举倍数解决,复杂度O(nlogn)O(nlogn)的。其实排序询问可以节约时间,不过暴力也可以过。直接强行清空跑sumsum就好。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long

const int maxn=2e6+7;

using namespace std;

LL phi[maxn],f[maxn],sum[maxn],a[maxn];
LL not_prime[maxn],prime[maxn],low[maxn];
LL n,cnt,T;

void getf(LL n)
{
    phi[1]=1;
    f[1]=1; 
    for (LL i=2;i<=n;i++)
    {
        if (!not_prime[i])
        {
            prime[++cnt]=i;
            phi[i]=i-1;
            f[i]=phi[i]-phi[1];
            low[i]=i;
        }
        for (LL j=1;j<=cnt;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (low[i]%prime[j]==0) low[i*prime[j]]=low[i]*prime[j];
                               else low[i*prime[j]]=prime[j];
            if (i*prime[j]==low[i*prime[j]])
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                f[i*prime[j]]=phi[i*prime[j]]-phi[i];
            }
            else
            {
                LL x=low[i*prime[j]],y=i*prime[j]/x;
                f[i*prime[j]]=f[x]*f[y];
                phi[i*prime[j]]=phi[x]*phi[y];
            }
            if (i%prime[j]==0) break; 
        }
    }
}

int main()
{
    getf(2e6);  
    scanf("%lld",&T);   
    while (T--)
    {
        scanf("%lld",&n);
        LL ans=0;
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        for (LL i=1;i<=n;i++) a[phi[i]]++;      
        for (LL i=1;i<=n;i++)
        {
           for (LL j=i;j<=n;j+=i) sum[i]+=a[j];
        }
        for (LL i=1;i<=n;i++) ans+=sum[i]*sum[i]*f[i];
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值