原题:hdu 6390
题意:
定义 求:
解析:
欧拉函数的公式为:
那么易得:
那么我们要处理的就是 时的 ,以及有多少对a,b的gcd为i
一 : 求 时的 |
所以我们可以用预处理欧拉函数差不多的方法预处理出G值
for(D i=2;i<=MAXN;i++){// v为G值
inv[i]=(mod-mod/i)*inv[mod%i]%mod;//处理逆元
if(!vis[i]){
pri[++now]=i;
v[i]=i*inv[i-1]%mod;//质数的G
}
for(D j=1;j<=now&&pri[j]*i<=MAXN;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
v[i*pri[j]]=v[i];// i|pri[j]那么i*pri[j]的质因子==i的质因子
break;
}
v[i*pri[j]]=v[i]*v[pri[j]]%mod;//i没包括pri[j]这个质因子的时候
}
}
二 : 求 |
对于一个数i,在 的范围内,
显然 :
那么我们从大到小维护 ,因为我们要的是 的对数,所以要把 的情况减去
for(D i=1;i<=MAXN;i++){
f[i]=(n/i)*(m/i)%mod;
}
for(D i=MAXN/2;i>=1;i--){//最高到MAXAN/2有2i的情况
for(D j=i+i;j<=MAXN;j+=i){
f[i]-=f[j];
if(f[i]<0)f[i]+=mod;
}
}
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<sstream>
#include<functional>
using namespace std;
#define D long long
#define F double
#define mmm(a,b) memset(a,b,sizeof(a))
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
const int N=1000000;
const F pi=acos(-1);
D n,m,p,mi;
D f[N+9],v[N+9],inv[N+9];
D pri[N+9],now;
bool vis[N+9];
void init(){
mmm(vis,0);mi=min(m,n);now=0;
for(D i=1;i<=mi;i++){
f[i]=(n/i)*(m/i)%p;
}
for(D i=mi/2;i>=1;i--){
for(D j=i+i;j<=mi;j+=i){
f[i]-=f[j];
if(f[i]<0)f[i]+=p;
}
}
v[1]=inv[1]=1;
for(D i=2;i<=mi;i++){
inv[i]=(p-p/i)*inv[p%i]%p;
if(!vis[i]){
pri[++now]=i;
v[i]=i*inv[i-1]%p;
}
for(D j=1;j<=now&&pri[j]*i<=mi;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
v[i*pri[j]]=v[i];
break;
}
v[i*pri[j]]=v[i]*v[pri[j]]%p;
}
}
}
int main(){
int t=read();
while(t--){
n=read(),m=read(),p=read();
init();
D ans=0;
for(D i=1;i<=mi;i++)
ans=(ans+f[i]*v[i])%p;
printf("%lld\n",ans);
}
}