题意:给出函数,求值,取模
推导过程:
细节详见代码。
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>
#include <climits>
using namespace std;
#define LL long long
const int maxn=1000005;
long long f[maxn],pre[maxn],inv[maxn];
int phi[maxn],u[maxn],prime[maxn],p;
bool vis[maxn];
void init1(){
memset(vis, 0, sizeof vis);
memset(u,0,sizeof u);
for(int i=2;i<maxn;i++) if(!vis[i])
for(int j=i;j<maxn;j+=i){
if(u[j]==-1) continue;
if((j/i)%i==0) u[j]=-1;
else u[j]++;
vis[j]=1;
}
for(int i=1;i<maxn;i++){
if(u[i]==-1) u[i]=0;
else if(u[i]%2) u[i]=-1;
else u[i]=1;
}
}
void init2(){
int tot=0;
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 && prime[j]*i*1LL<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;
}
}
}
}
LL ans;
int n,m;
int main(){
init1(); //Mobius G(x)系数打表
init2(); //欧拉打表 复杂度O(nlogn)
int _; cin>>_;
while(_--){
ans=0;
scanf("%d%d%d",&n,&m,&p);
inv[1] = 1;
for (int i=2; i<=n; ++i) {
inv[i] = 1LL* (p - p / i) * inv[p%i] % p; //线性逆元表 O(n)复杂度
}
int N=min(m,n); //gcd(a,b)范围为1~min(a,b)
for(int i=1;i<=N;i++) f[i]=1LL*(m/i)*(n/i); //预处理
memset(pre,0,sizeof pre);
for(int i=1;i<=N;i++){
for(int j=i;j<=N;j+=i){
pre[i]+=1ll*(f[j]*u[j/i]); //二重预处理
}
}
for(int i=1;i<=N;i++) pre[i]%=p;//统一取模,由于u[]震荡性,不会溢出
ans=0;
for(int i=1;i<=N;i++){
//将每一个gcd(a,b)==i的对数进行累加
ans=(1LL*ans+1LL*i*inv[phi[i]]%p*pre[i]%p)%p;
}
//防止意外
while(ans<0) ans+=p;
printf("%lld\n",ans);
}
return 0;
}