传送门
Description
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
Input
第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个正整数,分别为a,b,d。(1<=d<=a,b<=50000)
Output
对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。
Sample Input
2
4 5 2
6 4 3
Sample Output
3
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(6,3),(3,3)。
这个题就是求 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = k ] ( n < m ) \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=k] (n<m) ∑i=1n∑j=1m[gcd(i,j)=k](n<m)
而对于gcd有 ∑ d ∣ g c d ( i , j ) μ ( d ) = [ g c d ( i , j ) = 1 ] \sum_{d|gcd(i,j)} \mu(d)=[gcd(i,j)=1] ∑d∣gcd(i,j)μ(d)=[gcd(i,j)=1],因为gcd不为1的时候求和为0,具体证明就不给出了
所以对于gcd(i,j)=k只需要把k提取出去。
∑
i
=
1
n
∑
j
=
1
m
g
c
d
(
i
,
j
)
=
k
∑
i
=
1
n
/
k
∑
j
=
1
m
/
k
g
c
d
(
i
,
j
)
=
1
\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=k\\ ~\\ \sum_{i=1}^{n/k}\sum_{j=1}^{m/k}gcd(i,j)=1
i=1∑nj=1∑mgcd(i,j)=k i=1∑n/kj=1∑m/kgcd(i,j)=1
然后就可以将gcd换了
∑
i
=
1
n
/
k
∑
j
=
1
m
/
k
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
\sum_{i=1}^{n/k}\sum_{j=1}^{m/k}\sum_{d|gcd(i,j)} \mu(d)
i=1∑n/kj=1∑m/kd∣gcd(i,j)∑μ(d)
然后我们可以枚举d,假设n小于m,又因为i,j都是d的倍数,所以有
∑
d
=
1
n
/
k
μ
(
d
)
∑
i
=
1
n
k
d
∑
j
=
1
m
k
d
\sum_{d=1}^{n/k} \mu(d)\sum_{i=1}^\frac{n}{kd}\sum_{j=1}^\frac{m}{kd}
d=1∑n/kμ(d)i=1∑kdnj=1∑kdm
然后对这个式子化简就有
∑
d
=
1
n
/
k
μ
(
d
)
∗
⌊
n
k
d
⌋
∗
⌊
m
k
d
⌋
\sum_{d=1}^{n/k}\mu(d)*\lfloor \frac{n}{kd}\rfloor *\lfloor \frac{m}{kd}\rfloor
d=1∑n/kμ(d)∗⌊kdn⌋∗⌊kdm⌋
化简到这一步之后我们可以通过分块,即将式子中相同的部分直接计算出结果,这样复杂度就可降到
O
n
O\sqrt{n}
On
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=50000;
int miu[maxn+10];
int vis[maxn+10];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void mobius()
{
for(int i=1;i<=maxn;++i)
miu[i]=1;
for(int i=2;i<=maxn;++i)
{
if(!vis[i])
{
miu[i]=-1;
for(int j=i+i;j<=maxn;j+=i)
{
vis[j]=1;
if((j/i)%i==0) miu[j]=0;
else miu[j]*=-1;
}
}
}
for(int i=1;i<=maxn;i++) miu[i]+=miu[i-1];
}
int main()
{
// ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
int t,a,b,d,m;
mobius();
t=read();
while(t--){
int ans=0;
a=read(),b=read(),d=read();
a/=d,b/=d;
int n=min(a,b),r=0;
for(int i=1;i<=n;i=r+1){
r=min(a/(a/i),b/(b/i));
ans+=(miu[r]-miu[i-1])*(a/i)*(b/i);
}
printf("%d\n",ans);
}
return 0;
}