【2018 Multi-University Training Contest 7】GuGuFishtion(莫比乌斯反演)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Coldfresh/article/details/81634524

Problem Description题目链接
Today XianYu is too busy with his homework, but the boring GuGu is still disturbing him!!!!!!
At the break time, an evil idea arises in XianYu’s mind.
‘Come on, you xxxxxxx little guy.’
‘I will give you a function ϕ(x) which counts the positive integers up to x that are relatively prime to x.’
‘And now I give you a fishtion, which named GuGu Fishtion, in memory of a great guy named XianYu and a disturbing and pitiful guy GuGu who will be cooked without solving my problem in 5 hours.’
‘The given fishtion is defined as follow:

G u ( a , b ) = ϕ ( a b ) ϕ ( a ) ϕ ( b )

And now you, the xxxxxxx little guy, have to solve the problem below given m,n,p.’

( a = 1 m b = 1 n G u ( a , b ) ) ( mod p )

So SMART and KINDHEARTED you are, so could you please help GuGu to solve this problem?
‘GU GU!’ GuGu thanks.

Input
Input contains an integer T indicating the number of cases, followed by T lines. Each line contains three integers m,n,p as described above.
1≤T≤3
1≤m,n≤1,000,000
m a x ( m , n ) < p 1 , 000 , 000 , 007
And given p is a prime.

Output
Please output exactly T lines and each line contains only one integer representing the answer.

Sample Input
1
5 7 23

Sample Output
2
窝的做法似乎很麻烦啊。没有看出像题解那样的。
先考虑

ϕ ( a b ) ϕ ( a ) ϕ ( b )

当然a和b互质的话当然为1,如果不互质么,根据欧拉函数的公式,显然有
1 1 1 p 1 1 1 1 p 2 . . . 1 1 1 p k

p1到pk是a和b公有的不同质因子。这个似乎和有gcd有点关系但是又不太一样。
假设gcd是4,但是用到的是2,gcd是12,用到的是6,即2*3,
那么我们先来考虑这个东西,就是假设 x 是若干单个质因子的乘积。那么上个表达式显然就是 x ϕ ( x )
窝设
l ( x ) = p p | x p

那么假设我们枚举每个gcd,那么对应要乘的东西就是

l ( x ) ϕ ( l ( x ) )

窝设为 g ( x ) ,即
g ( x ) = l ( x ) ϕ ( l ( x ) )

显然这个函数是积性函数就可以筛了,
然而根本不用那么麻烦,实际上
g ( x ) = x ϕ ( x )

窝当时竟然没有看出来,现在才想起来。不过去筛的时候可以完全忽略这些,因为g就是积性函数,不管哪种理解,对筛g这个函数没有影响。
所以现在我们就要去枚举每个gcd的贡献了,

那么

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

把gcd拿出来:
a n s = k = 1 g ( k ) i j g c d ( i , j ) == k

那么就是求gcd贡献的经典套路。
f ( x ) 为gcd为x的个数, F ( x ) 为gcd是x的倍数
反演一下:
f ( n ) = n | d μ ( d n ) F ( d )

所以:
a n s = k = 1 g ( k ) k | d μ ( d k ) F ( d )

把F拿到外面来:
a n s = d = 1 F ( d ) k | d μ ( d k ) g ( k )

G ( x ) = k | x μ ( x k ) g ( k )

显然这是两个积性函数的狄利克雷卷积,G也是积性函数,预处理都筛出来,在分块求和

之前还用了一个 O ( n n ) 的做法,居然T了,然后改用这个反演,竟然还卡常。。。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxx 1000005
#define ll long long
using namespace std;
bool isP[maxx];
int prime[maxx];
int phi[maxx];
int mu[maxx];
int cnt;
ll g[maxx];
ll G[maxx];
ll P(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
void init()
{
    mu[1]=phi[1]=1;
    for(int i=2;i<maxx;i++)
    {
        if(!isP[i])
        {
            prime[cnt++]=i;
            mu[i]=-1;
            phi[i]=i-1;
        }
        for(int j=0;j<cnt&&(ll)i*prime[j]<maxx;j++)
        {
            int now=i*prime[j];
            isP[now]=true;
            if(i%prime[j])
            {
                mu[now]=-mu[i];
                phi[now]=phi[i]*phi[prime[j]];
            }
            else
            {
                mu[now]=0;
                phi[now]=phi[i]*prime[j];
                break;
            }
        }
    }
}
int _min;
int n,m,p;
void _init()
{
    memset(G,0,sizeof(G));
    memset(g,0,sizeof(G));
    g[1]=1;
    for(int i=2;i<=_min;i++)
    {
        if(!isP[i]) g[i]=i*P(phi[i],p-2,p)%p;
        for(int j=0;j<cnt&&(ll)i*prime[j]<=_min;j++)
        {
            if(i%prime[j])
                g[i*prime[j]]=g[i]*g[prime[j]]%p;
            else
            {
                g[i*prime[j]]=g[i];
                break;
            }
        }
    }//筛g函数
    for(int i=1;i<=_min;i++)
    {
        for(int j=i;j<=_min;j+=i)
            if(mu[j/i])
                G[j]=(G[j]+mu[j/i]*g[i]%p)%p;
    }//筛G函数
    for(int i=1;i<=_min;i++)
    {
        G[i]=(G[i]+G[i-1])%p;
        if(G[i]<0)G[i]+=p;
    }//前缀和
}
int main()
{
    int t;
    init();
    //for(int i=1;i<=50;i++)cout<<phi[i]<<endl;
    cin>>t;
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&p);
        ll ans=0;
        _min=min(n,m);
        _init();
        for(int i=1,last;i<=_min;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            ll temp=(ll)(n/i)*(m/i)%p*(G[last]-G[i-1]+p)%p;
            ans=(ans+temp)%p;
        }        
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Coldfresh/article/details/81634524