Description
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
Input
第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k
Output
共n行,每行一个整数表示满足要求的数对(x,y)的个数
Solution
看到
∑
\sum
∑和gcd果断莫比乌斯反演。
考虑到有下界不好搞,我们差分一下。
sum([i,j],[k,l])表示对于满足i≤x≤j,k≤y≤l,且gcd(x,y) = k的数对(x,y)个数
ans=sum([1,b],[1,d])-sum([1,b],[1,c-1])-sum([1,a-1],[1,d])+sum([1,a-1],[1,c-1])
现在查询变成了sum([1,n],[1,m])这样子,假设n>m,然后推推式子:
∑
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
]
=
∑
i
=
1
⌊
n
k
⌋
∑
j
=
1
⌊
m
k
⌋
∑
l
∣
i
,
l
∣
j
μ
(
l
)
=
∑
l
=
1
⌊
m
k
⌋
∑
i
=
1
⌊
⌊
n
k
⌋
l
⌋
∑
i
=
1
⌊
⌊
m
k
⌋
l
⌋
μ
(
l
)
=
⌊
⌊
n
k
⌋
l
⌋
×
⌊
⌊
m
k
⌋
l
⌋
×
∑
l
=
1
⌊
m
k
⌋
μ
(
l
)
\ \ \ \ \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==k]\\=\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}[gcd(i,j)==1]\\=\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{l|i,l|j}\mu(l)\\=\sum_{l=1}^{\lfloor\frac{m}{k}\rfloor}\sum_{i=1}^{\lfloor\frac{\lfloor\frac{n}{k}\rfloor}{l}\rfloor}\sum_{i=1}^{\lfloor\frac{\lfloor\frac{m}{k}\rfloor}{l}\rfloor}\mu(l)\\=\lfloor\frac{\lfloor\frac{n}{k}\rfloor}{l}\rfloor\times \lfloor\frac{\lfloor\frac{m}{k}\rfloor}{l}\rfloor\times\sum_{l=1}^{\lfloor\frac{m}{k}\rfloor}\mu(l)
∑i=1n∑j=1m[gcd(i,j)==k]=∑i=1⌊kn⌋∑j=1⌊km⌋[gcd(i,j)==1]=∑i=1⌊kn⌋∑j=1⌊km⌋∑l∣i,l∣jμ(l)=∑l=1⌊km⌋∑i=1⌊l⌊kn⌋⌋∑i=1⌊l⌊km⌋⌋μ(l)=⌊l⌊kn⌋⌋×⌊l⌊km⌋⌋×∑l=1⌊km⌋μ(l)
那么我们直接先把abcd都除个k。
然后一般套路整除分块就完事了。
Code
#include<bits/stdc++.h>
using namespace std;
int p[10000010],cnt;
bool vis[10000010];
int miu[10000010];
int s[10000010];
void work(int maxm){
miu[1]=1;
for(int i=2;i<=maxm;i++){
if(!vis[i]){
p[cnt++]=i;
miu[i]=-1;
}
for(int j=0;j<cnt&&p[j]*i<=maxm;j++){
vis[p[j]*i]=1;
if(i%p[j]==0){
miu[p[j]*i]=0;
break;
}
miu[p[j]*i]=-miu[i];
}
}
for(int i=1;i<=maxm;i++)
s[i]=s[i-1]+miu[i];
}
int calc(int n,int m){
int l=1,r;
int res=0;
for(;l<=m;l=r+1){
r=min(n/(n/l),m/(m/l));
res+=(n/l)*1ll*(m/l)*(s[r]-s[l-1]);
}
return res;
}
int main(){
int t,a,b,c,d,k;
work(1e5);
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
int ans=0;
b/=k,d/=k;
a=(a-1)/k;
c=(c-1)/k;
ans+=calc(max(b,d),min(b,d));
ans+=calc(max(a,c),min(a,c));
ans-=calc(max(a,d),min(a,d));
ans-=calc(max(b,c),min(b,c));
printf("%d\n",ans);
}
}
/*
1
3 10 5 8 2
*/