做法:
- 首先根据唯一分解定理可知道,每个正整数都可以分解成质数幂的成绩
- 然后我们带入到原式中去
(原谅我的字不好看(T▽T))
- 我们就可以推出结论
- 听了dls的直播讲解后,说一下我的理解:
- 我们知道原暴力求答案复杂度是O(n*m)
- 但是转换成结论后,我们可以根据gcd的性质来求解
- 我们知道gcd(a,b) = d, d一定是a,b的公共因子,也一定能写成质因数分解的形式。
- 且d 的值一定在[1, min(a,b)]
- 这时候我们可以通过枚举min(a,b)的因子来求解。咱么求呢,没错容斥一下
- 这也是我想不到的地方,%% dls
- 这里我们设f[d] 表示的是gcd(a,b) = d 的倍数 的对数 (不是数学的“对数”,是几对数的对数!)
- 那么这个对数怎么求呢?欧拉函数的求解过程没忘吧(~ ̄▽ ̄)~
- 当当~ f[d] = (a/d)*(b/d) (懂欧拉函数求解的小伙伴一定能看懂,就不多说啦)
- F[d] 表示的是gcd(a,b) = d 的对数
- 显然F[d] = f[d] - f[2*d] - f[3*d] …… f[n*d] (容斥一下)
- 这里的n*d是什么呢,是min(a,b)呀,因为它最大也就是min(a,b)
- 所以对于每一个d, 结果就是 ans = d*inv(phi(d))%mod ヾ(✿゚▽゚)ノ
AC代码:
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const double PI = 4*atan(1.0);
const int maxn = 1e6+5;
const int INF = 0x3f3f3f3f;
int prime[maxn];
int phi[maxn];
ll inv[maxn];
ll f[maxn];
int tot,m,n,p;
int mmin,ans;
void phi_table()
{
phi[1] = 1;
for(int i=2;i<maxn;i++)
{
if(!phi[i]){
phi[i] = i-1;
prime[tot++] = i;
}
for(int j=0;j<tot && 1LL*i*prime[j]<maxn;j++){
if(i%prime[j]) phi[i*prime[j]] = phi[i]*(prime[j]-1);
else{
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
}
}
}
void inv_table()
{
inv[1] = 1;
for(int i=2;i<=mmin;i++)
inv[i] = 1LL*(p-p/i)*inv[p%i]%p;
}
int main()
{
#ifdef LOCAL_FILE
freopen("in.txt","r",stdin);
#endif // LOCAL_FILE
ios_base::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
phi_table();
int t;
cin>>t;
while(t--)
{
cin>>m>>n>>p;
mmin = min(m,n);
inv_table();
ans = 0;
for(int i=mmin;i>=1;i--)
{
f[i] = (1LL)*(n/i)*(m/i);
for(int j=i+i;j<=mmin;j+=i) f[i]-=f[j];
ans=(ans%p+i*f[i]%p*inv[phi[i]]%p)%p;
}
cout<<ans<<endl;
}
return 0;
}