Description
求∑a=1m∑b=1nφ(ab)φ(a)φ(b)∑a=1m∑b=1nφ(ab)φ(a)φ(b)
Input
第一行一整数TT表示用例组数,每组用例输入三个整数,其中pp是素数
Output
输出答案,结果模pp
Sample Input
1
5 7 23
Sample Output
2
Solution
假设a=pa11...parrqb11...qbss,b=pc11...pcrrqd1s+1...qdts+ta=p1a1...prarq1b1...qsbs,b=p1c1...prcrqs+1d1...qs+tdt,其中p1,...,pr,q1,...,qs+tp1,...,pr,q1,...,qs+t为互不相同的素数,那么有φ(ab)=ab∏i=1r(1−1pi)∏j=1s+t(1−1qj)φ(ab)=ab∏i=1r(1−1pi)∏j=1s+t(1−1qj),而φ(a)φ(b)=ab(∏i=1r(1−1pi))2∏j=1s+t(1−1qj)φ(a)φ(b)=ab(∏i=1r(1−1pi))2∏j=1s+t(1−1qj),故有
那么答案即为
其中f(m,n)f(m,n)表示满足1≤a≤m,1≤b≤n,gcd(a,b)=11≤a≤m,1≤b≤n,gcd(a,b)=1的二元组(a,b)(a,b)的对数
由莫比乌斯反演及分块加速即可O(min(m,n)−−−−−−−−√)O(min(m,n))得到f(m,n)f(m,n),注意到1≤φ(d)<min(m,n)<p1≤φ(d)<min(m,n)<p,故可以线性预处理模pp逆元,时间复杂度
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 1000005
int euler[maxn],prime[maxn],res,mu[maxn],sum[maxn];
void get_euler(int n=1e6)
{
mu[1]=sum[1]=1;
euler[1]=1;
res=0;
for(int i=2;i<=n;i++)
{
if(!euler[i])euler[i]=i-1,prime[res++]=i,mu[i]=-1;
for(int j=0;j<res&&prime[j]*i<=n;j++)
{
if(i%prime[j])
{
euler[prime[j]*i]=euler[i]*(prime[j]-1);
mu[prime[j]*i]=-mu[i];
}
else
{
euler[prime[j]*i]=euler[i]*prime[j];
mu[prime[j]*i]=0;
break;
}
}
sum[i]=sum[i-1]+mu[i];
}
}
int T,n,m,p;
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/p*p;
}
int add(int x,int y)
{
x+=y;
if(x>=p)x-=p;
return x;
}
int inv[maxn];
void init(int n)
{
inv[0]=0;
inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=mul(p-p/i,inv[p%i]);
}
int f(int a,int b)
{
if(a>b)swap(a,b);
int ans=0;
for(int i=1,next=0;i<=a;i=next+1)
{
next=min(a/(a/i),b/(b/i));
int temp=((sum[next]-sum[i-1])%p+p)%p;
ans=add(ans,mul(mul(a/i,b/i),temp));
}
return ans;
}
int main()
{
get_euler();
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&p);
init(min(n,m));
int ans=0;
for(int d=1;d<=min(n,m);d++)
{
int num=f(n/d,m/d);
ans=add(ans,mul(mul(d,num),inv[euler[d]]));
}
printf("%d\n",ans);
}
return 0;
}