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

题意:
给出 n ( n <= 2 10 6 ) ,求
i = 1 n j = 1 n ϕ ( g c d ( ϕ ( i ) , ϕ ( j ) ) ) 的值。

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

a n s = i = 1 n j = 1 n ϕ ( g c d ( ϕ ( i ) , ϕ ( j ) ) )

= D = 1 n ϕ ( D ) i = 1 n j = 1 n [ g c d ( ϕ ( i ) , ϕ ( j ) ) == D ]

然后我们枚举 ϕ ( i ) ϕ ( j ) ,我们设 a d = i = 1 n [ ϕ ( i ) == d ] x = p h i ( i ) y = p h i ( j ) ,其实可以理解为换元,则原式可以变为,
= D = 1 n ϕ ( D ) x = 1 n y = 1 n [ g c d ( x , y ) == D ] a x a y

然后就是很显然的反演了,我们设 s u m i = i | t a t
= D = 1 n ϕ ( D ) d = 1 n μ ( d ) ( s u m D d ) 2

T = D d ,则
= T = 1 n ( s u m T ) 2 d | T μ ( d ) ϕ ( T / d )

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

代码:

#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);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81455611
今日推荐