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