[POI2007]ZAP-Queries
洛谷题目传送门
密码学家正在尝试破解一种叫 BSA 的密码。
他发现,在破解一条消息的同时,他还需要回答这样一种问题:
给出 a , b , d a,b,d a,b,d,求满足 1 ≤ x ≤ a , 1 ≤ y ≤ b 1 \leq x \leq a,1 \leq y \leq b 1≤x≤a,1≤y≤b,且 gcd ( x , y ) = d \gcd(x,y)=d gcd(x,y)=d的二元组 ( x , y ) (x,y) (x,y) 的数量。
因为要解决的问题实在太多了,他便过来寻求你的帮助。
解题思路
题目要求
∑
i
=
1
a
∑
j
=
1
b
[
gcd
(
i
,
j
)
=
=
k
]
\sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j)==k]
i=1∑aj=1∑b[gcd(i,j)==k]
设
f
(
k
)
=
∑
i
=
1
a
∑
j
=
1
b
[
gcd
(
i
,
j
)
=
=
k
]
f(k)=\sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j)==k]
f(k)=i=1∑aj=1∑b[gcd(i,j)==k]
设 F ( k ) = ∑ i = 1 a ∑ j = 1 b [ k ∣ gcd ( i , j ) ] F(k)=\sum_{i=1}^a \sum_{j=1}^b [\;k\;|\;\gcd(i,j)] F(k)=i=1∑aj=1∑b[k∣gcd(i,j)]
由莫比乌斯反演式子:
若 F ( n ) = ∑ n ∣ d f ( d ) F(n)=\sum_{n|d}f(d) F(n)=∑n∣df(d) , 则 f ( n ) = ∑ n ∣ d μ ( n d ) F ( d ) f(n)=\sum_{n|d}\mu(\frac{n}{d})F(d) f(n)=∑n∣dμ(dn)F(d)
代进去
f
(
n
)
=
∑
d
μ
(
d
)
⌊
a
d
⌋
⌊
b
d
⌋
f(n)=\sum_d \mu(d) \lfloor \frac{a}{d} \rfloor \lfloor \frac{b}{d} \rfloor
f(n)=d∑μ(d)⌊da⌋⌊db⌋
套整除分块就行了
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int u[N],vis[N];
int prime[N],top=0;
int sum[N];
void sieve(int n)
{
u[1]=1;
for(int i=2;i<=n;i++)
{
if(vis[i]==0)
{
prime[++top]=i;
u[i]=-1;
}
for(int j=1;j<=top&&prime[j]*i<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)
{
u[i*prime[j]]=0;
break;
}
else u[i*prime[j]]=u[i]*u[prime[j]];
}
}
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+u[i];
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
sieve(1e5+7);
int T;
cin>>T;
while(T--)
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
a/=d;
b/=d;
int res=0;
if(a>b) swap(a,b);
for(int L=1,R=0;L<=a;L=R+1)
{
R=min(a/(a/L),b/(b/L));
res=res+(sum[R]-sum[L-1])*(a/L)*(b/L);
}
printf("%d\n",res);
}
return 0;
}