题目: 给出n,m,p求
思路:phi(a)=a*(1-1/p1)*(1-1/p2)*.....*(1-1/pn) pi是a的素因子;
phi(a*b)=a*b*(1-1/p1)*(1-1/p2)*.....*(1-1/pm)里面包含了a和b的所有素因子(去重后的)。
phi(a*b) / (phi(a)*phi(b)) 约分后的结果就是1/[ (1-1/p1)*(1-1/p2)*...*(1-1/pv) ] 其中p1,p2...pv是a和b的gcd
然后这个题就是求从1-n选一个x和1-m选一个y使得gcd(x,y)=k的组数,枚举每一个k,每次跑min(n,m)/k次,总复杂度是O(nlogn)。
前面要预处理以每个数作为gcd时的贡献。 (第一次比赛时做出来莫比乌斯反演的题目...之前都是gg的,好菜。。)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000000;
bool check[MAXN+10];
int prime[MAXN+10];
int mu[MAXN+10];
void Mobius()
{
memset(check,false,sizeof(check));
mu[1] = 1;
int tot = 0;
for(int i = 2; i <= MAXN; i++)
{
if( !check[i] )
{
prime[tot++] = i;
mu[i] = -1;
}
for(int j = 0; j < tot; j++)
{
if(i * prime[j] > MAXN) break;
check[i * prime[j]] = true;
if( i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
}
#define ll long long
int b,d,t;
ll p;
ll inv[MAXN+10];
ll a[MAXN+10];
bool visit[MAXN+10];
void init(int N)
{
int i,j,n=N;
for(int i=0;i<=N;i++)
visit[i]=true;
for(int i=0;i<=N;i++)
a[i]=1;
for (i=2;i<=n;i++)
{
ll tmp=1ll*i*inv[i-1]%p;
if (visit[i])
{
a[i]=a[i]*tmp%p;
for (j=i+i;j<=n;j+=i)
{
a[j]=a[j]*tmp%p;
visit[j]=false;
}
}
}
}
int main()
{
Mobius();
scanf("%d",&t);
while(t--)
{
scanf("%d%d%I64d",&b,&d,&p);
inv[0]=inv[1]=1;
for(int i=2;i<=b;i++)
inv[i]=1ll*(p-p/i)*inv[p%i]%p;
init(b);
if(b>d) swap(b,d);
ll ans=0;
for(int k=1;k<=b;k++)
{
int bb=b/k,dd=d/k;
ll num=0;
for(int i=1;i<=bb;i++)
num+=(ll)mu[i]*(bb/i)*(dd/i);
num%=p;
ans+=num*a[k]%p;
if(ans>p)
ans-=p;
}
printf("%I64d\n",ans);
}
return 0;
}