Description
求有多少个数对 ((x,y)) ,满足$ a leq x leq b$ ,(c leq y leq d) ,且 (gcd(x,y) = k),(gcd(x,y))函数为 (x) 和 (y) 的最大公约数。多组询问。(a,b,c,d,k,T leq 50000)
Solution
莫比乌斯反演的经典题目QAQ
首相将问题转化成前缀上的问题。即需要求出 有多少个数对 ((x,y)) ,满足$ 1 leq x leq a$ ,(1 leq y leq b) ,且 (gcd(x,y) = k)。如果能够快速算出来这个,容斥一下就可以求出最后答案。
考虑这个怎么求,开始推式子。这个东西显然就是
[sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[gcd(i,j)=k]
]
把 (k) 提出来可得
[sumlimits_{i=1}^{lfloorfrac{n}{k}
floor}sumlimits_{j=1}^{lfloorfrac{m}{k}
floor}[gcd(i, j)=1]
]
然后把后面这个 ([gcd(i,j)=1]) 反演掉,得
[sumlimits_{i=1}^{lfloorfrac{n}{k}
floor}sumlimits_{j=1}^{lfloorfrac{m}{k}
floor}sumlimits_{d|gcd(i,j)}mu(d)
]
把 (d) 搞到前面来,得到
[sumlimits_{d=1}^{lfloor frac{n}{k}
floor} mu(d)lfloor frac{n}{kd}
floorlfloor frac{m}{kd}
floor
]
好了,这个玩意可以预处理出 (mu) 得前缀和然后分块完事。
Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 50000;
int k, cnt, p[N + 50], mu[N + 50], flag[N + 50], sum[N + 50];
inline void prework() {
flag[1] = mu[1] = 1;
for(int i = 2; i <= N; i++) {
if(!flag[i]) {
p[++cnt] = i; mu[i] = -1;
} for(int j = 1; j <= cnt && i * p[j] <= N; j++) {
flag[i * p[j]] = 1;
if(i % p[j] == 0) {
mu[i * p[j]] = 0; break;
} mu[i * p[j]] = mu[i] * -1;
}
} for(int i = 1; i <= N; i++) sum[i] = sum[i - 1] + mu[i];
}
inline ll calc(int n, int m) {
if(n > m) swap(n, m); ll ret = 0;
for(int l = 1, r; l <= n / k; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ret += 1ll * (n / (l * k)) * (m / (l * k)) * (sum[r] - sum[l - 1]);
} return ret;
}
int main() {
int T; prework();
scanf("%d", &T);
while(T--) {
int a, b, c, d;
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
printf("%lld
", calc(a - 1, c - 1) - calc(b, c - 1) - calc(d, a - 1) + calc(b, d));
}
return 0;
}