HDU 6390 莫比乌斯反演

题目链接

题意:

定义G(a,b)=ϕ(ab)/ϕ(a)*ϕ(b),给定n,m,mod求mod意义下的Σ(a=1->n)Σ(b=1->m)G(a,b)

思路:

ϕ(a) = a*(1-1/p1)*(1-1/p2)*...*(1-1/px)

ϕ(b) = b*(1-1/p1)*(1-1/p2)*...*(1-1/px)

ϕ(ab) = ab*(1-1/p1)*(1-1/p2)*...*(1-1/px)

上下约分就会发现表达式就变成了1/(1-1/Pgcd1)*(1-1/Pgcd2)*...*(1-1/Pgcdx)

发现这个表达式以后我们先预处理各个值在mod意义下此表达式的值

因为上面的表达式是针对GCD的,所以我们要求各个GCD的对数了

求GCD的对数是一个很经典的莫比乌斯问题,所以我们只要枚举GCD然后套用莫比乌斯求n/GCD和m/GCD范围内互质对数即可

复杂度计算预处理值我们需要一个调和级数级别的复杂度即O(nlogn),套用莫比乌斯时是将调和级数各项开根号再相加所以一定是小于等于预处理的,最终复杂度为O(nlogn)级别

C++代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000010;
int Min( int a , int b ){ return a<b?a:b; }

int isprime[maxn],primes[maxn],mu[maxn],len;

void Get_mu()
{
    mu[0] = 0,mu[1] = 1,len = 0;
    for ( int i=2 ; i<maxn ; i++ )
        isprime[i] = 1;
    for ( int i=2 ; i<maxn ; i++ )
    {
        if ( isprime[i] )
            primes[++len] = i,mu[i] = -1;
        for ( int j=1 ; j<=len ; j++ )
        {
            if ( i*primes[j]>maxn )
                break;
            isprime[i*primes[j]] = 0;
            if ( i%primes[j]==0 )
            {
                mu[i*primes[j]] = 0; break;
            }
            else
            {
                mu[i*primes[j]] = -mu[i];
            }
        }
    }
    for ( int i=2 ; i<maxn ; i++ )
        mu[i] += mu[i-1];
}

int n,m,mod,inv[maxn],sum[maxn];

int main()
{
    Get_mu();
    for ( int T ; scanf ( "%d" , &T )==1 ; )
    {
        for ( int cas=1 ; cas<=T ; cas++ )
        {
            scanf ( "%d%d%d" , &n , &m , &mod );
            inv[1] = 1;
            sum[1] = 1;
            for ( int i=2 ; i<maxn ; i++ )
                inv[i] = 1LL*inv[mod%i]*(mod-mod/i)%mod,sum[i] = 1;
            for ( int i=2 ; i<maxn ; i++ )
            {
                if ( isprime[i] )
                {
                    for ( int j=i ; j<maxn ; j+=i )
                        sum[j] = 1LL*sum[j]*i%mod*inv[i-1]%mod;
                }
            }
            int ans = 0;
            if ( n>m ) swap( n , m );
            for ( int k=1 ; k<=n ; k++ )
            {
                int p = n/k;
                int q = m/k;
                int cnt = 0,last;
                for ( int i=1 ; i<=p ; i=last+1 )
                {
                    last = Min( p/(p/i) , q/(q/i) );
                    cnt = ( cnt+1LL*(mu[last]-mu[i-1])*(p/i)%mod*(q/i)%mod )%mod;
                }
                ans = ( ans+1LL*cnt*sum[k] )%mod;
            }
            printf ( "%d\n" , ans );
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Game_Acm/article/details/81674635