Description
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
Solution
首先可以想到答案可以拆开来算,那么就是
∑ni=1∑mj=1[gcd(i,j)=k]
但是这样化简不好算,考虑另一种思路
设
f(i)
为
gcd(x,y)=i
的数对的数量,
F(i)
为
i|gcd(x,y)
的数对数量
显然有
F(i)=⌊ni⌋⌊mi⌋
,且
F(i)=∑i|df(d)
,那么就是莫比乌斯反演的套路了
莫比乌斯反演有两种
一种是
F(n)=∑d|nf(d)⇔f(n)=∑d|nμ(d)F(nd)
第二种是
F(n)=∑n|df(d)⇔f(n)=∑n|dμ(dn)F(d)
这里是第二种,化成
f(i)=∑i|dμ(di)⌊ni⌋⌊mi⌋
那么枚举k的倍数统计答案即可
注意到对于一个
∑ni=1⌊ni⌋
可以跳过相同的结果快速求解,时间是
O(n√)
级别的,那么两个相乘同理也可以这么做
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=50005;
bool not_prime[N];
int prime[N],sum[N];
short mu[N];
void get_mu() {
mu[1]=1;
rep(i,2,N-1) {
if (!not_prime[i]) {
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&prime[j]*i<N;j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) {
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
rep(i,1,N-1) sum[i]=sum[i-1]+mu[i];
}
int cal(int n,int m,int k) {
if (n>m) std:: swap(n,m);
int ret=0;
for (int i=1,j;i<=n/k;i=j+1) {
j=std:: min(n/(n/k/i)/k,m/(m/k/i)/k);
ret+=((n/k/i)*(m/k/i))*(sum[j]-sum[i-1]);
}
return ret;
}
int main(void) {
get_mu();
int T; scanf("%d",&T);
while (T--) {
int a,b,c,d,k;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d\n", cal(b,d,k)-cal(a-1,d,k)-cal(b,c-1,k)+cal(a-1,c-1,k));
}
return 0;
}