题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。。
将一个询问拆成四个询问,原问题即为:
∑ni=1∑mi=1[gcd(i,j)=k]
。
令
f(k)
为
gcd(i,j)=k
的方案数,
F(k)=k|gcd(i,j)
的方案数。
则
f(k)=∑k|dμ(d/k)∗F(d)=>f(1)=∑dμ(d)∗⌊⌊n/k⌋d⌋∗⌊⌊m/k⌋d⌋
。
后面这部分预处理
μ
的前缀和,跳着计数,就可以做到单组询问
O((√n))
,总复杂度
O(nlogn)
。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace shai {
const LL maxn = 55000;
LL p[maxn], tag[maxn], cnt, mob[maxn];
LL smob[maxn];
void gao() {
cnt = 0;
mob[1] = 1;
for(LL i = 2; i < maxn; i++) {
if(!tag[i]) {
p[cnt++] = i;
mob[i] = -1;
}
for(LL j = 0; j < cnt && i * p[j] < maxn; j++) {
tag[i*p[j]] = 1;
if(i % p[j] == 0) {
mob[i*p[j]] = 0;
break;
}
mob[i*p[j]] = -mob[i];
}
}
for(LL i = 1; i < maxn; i++)
smob[i] = smob[i-1] + mob[i];
}
}
namespace solver {
LL t;
LL a, b, c, d, k;
LL cal(LL n, LL m) {
n /= k;
m /= k;
LL res = 0;
LL last = 0;
for(LL i = 1; i <= min(n, m); i=last + 1) {
last = min(n/(n/i), m/(m/i));
res += (n/i)*(m/i) * (shai::smob[last] - shai::smob[i-1]);
}
return res;
}
void solve() {
scanf("%lld", &t);
for(LL _ = 0; _ < t; _++) {
scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k);
printf("%lld\n", cal(b, d) + cal(a-1, c-1) - cal(b, c-1) - cal(a-1, d));
}
}
}
int main() {
shai::gao();
solver::solve();
return 0;
}