【欧拉定理 && ( 容斥 || 反演 )】2018 Multi-University Training Contest 7 1005 GuGuFishtion

本文介绍了一个关于欧拉函数的应用问题,通过解析题目背景,利用质因数分解和欧拉函数的性质,将问题转化为求解特定数学序列的方法,并给出了具体的实现代码。

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

Step1 Problem:

phi():欧拉函数
G(a, b) = phi(a*b) / (phi(a) * phi(b))
让你求 sum{ G(a, b) }, a = 1, 2, 3, …, m, b = 1, 2, 3, …, n.
数据范围:
1<=m, n<=1e6, max(n, m) < p <= 1e9+7. 其中 p 是质数

Step2 Ideas:

逆元学习博客
本题参考博客
前置技能:phi[n] = n∏(1 - 1/p) 其中 p|n; n为质素时,phi[n] = n - 1;

phi[a*b] = a*b*∏(1 - 1/p1) 其中 p1|(a*b)
phi[a] = a*∏(1 - 1/p2) 其中 p2|a
phi[b] = b*∏(1 - 1/p3) 其中 p3|b
phi[a*b] / (phi[a]*phi[b]) = 1 / ∏(1 - 1/p); 其中 p 为 a 和 b 的公共质因数
1 / ∏(1 - 1/p) = ∏p / ∏(p-1);
p 为 a 和 b 的公共质因数,∏p = gcd(a, b); 由前置技能知道 ∏(p-1) = phi[gcd(a, b)];
所以: G(a, b) = phi(a*b) / (phi(a) * phi(b)) = gcd(a, b) / phi[gcd(a, b)];

f[i]: gcd(a, b) = i 的个数
问题就转换为:sum{ f[i]*i/phi[i] }, i = 1, 2, 3, …, min(n, m);
问题就是如何求 f[i]:
F[i]:gcd(a, b) = i 倍数的个数;
F[i] = (m/i) * (n/i); //也就是 i 倍数的个数 相乘
f[i] = F[i] - f[i的倍数 > i]

inv[n] = (M-M/i) * inv[M%i]%M; 其中 M 是质数
acdreamers 的推导过程:
M%i = M - (M/i) * i
(M/i)*i + M%i = M
令 t = M/i k = M%i
t*i + k ≡ 0(mod M)
-t * i ≡ k(mod M)
对上式两边同时除 i*k, 进一步得到
-t * inv[k] ≡ inv[i](mod M)
再把 t 和 k 替换掉,最终得到
inv[i] = (M - M/i)*inv[M%i]%M

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6+5;
int phi[N], inv[N];
ll f[N];
void get_phi()//求欧拉函数
{
    for(int i = 1; i < N; i++) phi[i] = i;
    for(int i = 2; i < N; i++)
    {
        if(phi[i] == i)
        {
            for(int j = i; j < N; j += i)
                phi[j] = phi[j]/i*(i-1);
        }
    }
}
int main()
{
    int T, n, m, MOD;
    get_phi();
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d %d", &n, &m, &MOD);
        inv[1] = 1;
        for(int i = 2; i <= n; i++)
            inv[i] = 1LL*(MOD - MOD/i)*inv[MOD%i]%MOD;
        if(n > m) swap(n, m);
        ll ans = 0;
        for(int i = n; i >= 1; i--)
        {
            f[i] = 1LL*(n/i)*(m/i);// gcd(a, b) == i的倍数的个数
            for(int j = 2*i; j <= n; j += i)// 容斥求 f[i]
                f[i] -= f[j];
            ans += f[i]%MOD*i%MOD*inv[phi[i]]%MOD; ans %= MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值