题目大意:
求对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。
题目分析:(莫比乌斯反演)
莫比乌斯反演公式:
常见的另一种形式(也是比较常用的一种):
我们要求:
设 :d=gcd(i,j)
那么gcd(i/d,j/d)==1
所以问题转化成:
设:n=a/d,m=c/d
求:
(接下来的东西,如果写的不对,各位神犇不要嘲笑我,欢迎指正,但是别问我是为什么这么做,我也是看别人的博客QWQ)
设:
所求的答案即为f(1)。
设:
根据设的内容我们可以认为F(n)代表n|gcd(i,j)的(i,j)的对数。
这个比较好求,可知:F(i)= (n/i)*(m/i)
根据莫比乌斯反演公式的第二种形式:(把n==1代入)
我们所求的答案即为:
即:
这时候我们只需要枚举d就可以了,当d大于n或m的时候F(d)就已经等于0了,所以只要枚举到min(n,m)就可以了,而且在(n/d)*(m/d)只有2 sqrt(n)种值,可以分块计算,u(d)可以线性筛出来然后求一个前缀和。
时间复杂度就是单次(sqrt(n))
此题解部分借鉴自hzwer的博客(当然,大神比我想的简单多了= =,直接利用莫比乌斯函数求和的那个性质就直接转过去了QWQ,好腻害好腻害的说)
代码如下:
#include <cstdio>
#include <algorithm>
#define N 50005
using namespace std;
int n,x,y,d;
int top,u[N],pri[N],sum[N];
bool b[N];
void linear_shaker()
{
u[1]=1;
for(int i=2;i<N;i++)
{
if(!b[i]) pri[++top]=i,u[i]=-1;
for(int j=1;j<=top && pri[j]*i<N;j++)
{
b[i*pri[j]]=true;
if(i%pri[j]==0) { u[i*pri[j]]=0; break; }
u[i*pri[j]]=-u[i];
}
}
for(int i=1;i<N;i++)
sum[i]=sum[i-1]+u[i];
}
int solve(int n,int m)
{
if(n>m) swap(n,m);
int ans=0,pos;
for(int i=1;i<=n;i=pos+1)
{
pos=min(n/(n/i),m/(m/i));
ans+=(sum[pos]-sum[i-1])*(n/i)*(m/i);
}
return ans;
}
int main()
{
scanf("%d",&n);
linear_shaker();
while(n--)
{
scanf("%d%d%d",&x,&y,&d);
printf("%d\n",solve(x/d,y/d));
}
return 0;
}