GuGuFishtion HDU - 6390(莫比乌斯反演+线性求逆元)

GuGuFishtion HDU - 6390

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 ) )     ( m o d     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 = 1 m b = 1 n G u ( a , b ) )     ( m o d     p )

其中

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

分析:

第一步:

首先我们必然是要观察函数

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

根据算术基本定理

假设
a = p 1 a 1 p 2 a 2 p 3 a 3

b = p 1 k 1 p 4 k 4 p 5 k 5

那么

G u ( a , b ) = ϕ ( p 1 a 1 + k 1 p 2 a 2 p 3 a 3 p 4 k 4 p 5 k 5 ) ϕ ( p 1 a 1 p 2 a 2 p 3 a 3 ) ϕ ( p 1 k 1 p 4 k 4 p 5 k 5 )

根据欧拉函数是积性函数的性质

如果 p q 互质,则 ϕ ( p q ) = ϕ ( p ) ϕ ( q )

因此上式最终化简得到

G u ( a , b ) = ϕ ( p 1 a 1 + k 1 ) ϕ ( p 1 a 1 ) ϕ ( p 1 k 1 )

再次根据欧拉函数的性质

素数的幂次的欧拉函数 ϕ ( p k ) = p k p k 1

因此上式再次化简得到

G u ( a , b ) = p 1 a 1 + k 1 ( 1 1 p 1 ) p 1 a 1 ( 1 1 p 1 ) p 1 k 1 ( 1 1 p 1 )

假设 a 1 < k 1 那么说明 p 1 a 1 是a,b的最大公因数

因此得到

G u ( a , b ) = p 1 a 1 p 1 a 1 ( 1 1 p 1 ) = g c d ( a , b ) ϕ ( g c d ( a , b ) )


第二步:

我们设 a [ i ] = i ϕ ( i )

原式化为

i = 1 m j = 1 n a [ g c d ( i , j ) ]   %   p

= d = 1 m i n ( m , n ) i = 1 m j = 1 n a [ d ] [ g c d ( i , j ) == d ]

[ g c d ( i , j ) == d ] 表示 m n d 的个数)

这个时候我们发现只需要枚举 g c d ,就可以求出 a [ i ]

剩下的目标就成了求 g c d = d 在范围 [ 1 , m ] [ 1 , n ] 的个数了

那么现在的问题就和题目HDU 1695 GCD一毛一样了

为了深化理解这里再次详细解释一下


第三步:

对于求解在 [ 1 , m ] [ 1 , n ] 范围内 g c d ( i , j ) = g 的个数

完全等价于在区间 [ 1 , m g ] , [ 1 , n g ] g c d ( i g , j g ) = 1

这时需要使用莫比乌斯反演

f ( d ) 表示 g c d = d 的个数

F ( d ) 表示 g c d = d d 的个数

我们知道在区间 [ 1 , m ] 范围内含有因子d及其d的倍数的个数为 m d

那么 F ( d ) = m d n d

根据莫比乌斯反演的倍数公式

F ( d ) = n | d f ( d ) f ( d ) = n | d μ ( d n ) F ( d )

根据上面的分析我么要求 f ( 1 ) n = 1

所以 f ( 1 ) = 1 | d μ ( d ) F ( d )

因此我们只需要枚举1的倍数(当然了就是 1 m i n n m 全加起来)

这里注意最一开始我们枚举的是gcd,本来要求gcd=g的个数,同除gcd将问题转化成gcd=1的个数

因此注意F(d)函数实际应为 F ( d ) = m g d n g d

因为时间卡的很紧,这里面因为涉及除法取模因此要求逆元,如果使用快速幂每次算是会超时的因此这里又学到了一个新的东西,线性筛逆元的方法

这样就效率就提高了很多


所以这道题目其实是HDU 1695 GCD这道题的多次求解。

code:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
int n,m;
ll p;
ll a[maxn],res[maxn],mu[maxn],inv[maxn];
void Euler(){
    memset(res,0,sizeof(res));
    res[1] = 1;
    for(int i = 2; i < maxn; i++) res[i] = i;
    for(int i = 2; i < maxn; i++){
        if(res[i] == i){
            for(int j = i; j < maxn; j += i){
                res[j] = res[j] / i * (i - 1);
            }
        }
    }
    mu[1] = 1;
    for(int i = 1; i < maxn; i++)
        for(int j = i * 2; j < maxn; j += i)
            mu[j] -= mu[i];
}

void init(){
    inv[1] = 1;
    for(int i = 2; i <= min(m,n); i++) inv[i] = inv[p%i] * (p - p / i) % p;
    for(int i = 1; i <= min(m,n); i++){
        a[i] = (ll)i * inv[res[i]] % p;
    }
}

ll get(int n,int m){
    ll ans = 0;
    for(int i = 1; i <= min(n,m); i++){
        ans += (ll)mu[i] * (n / i) * (m / i);
        ans %= p;
    }
    return ans;
}
int main(){
    Euler();
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%lld",&n,&m,&p);
        init();
        ll ans = 0;
        for(int i = 1; i <= min(n,m); i++){
            ans += (ll)a[i] * get(n/i,m/i);
            ans %= p;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81700226