题目大意:
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
分析:
设表示
那答案容斥一下可以知道就是
那么我们考虑求
=
我们知道莫比乌斯函数有一个性质,
,
那么当时,
显然
那么
就可以写成
然后这个东西可以转换一下,安利一个博客,
https://www.cnblogs.com/NaVi-Awson/p/8318709.html
然后我们对于所有的,可以发现取值最多只有种,并且相同的都是连续的一段,那么我们就可以愉快的预处理出函数的前缀和,然后分块处理,就可以求出 ,就可以让每次查询的复杂度都达到根号级别。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#define N 50005
using namespace std;
typedef long long ll;
int prime[N], check[N], sum[N], mu[N], T, a, b, c, d, k, cnt;
void Get_mobius()
{
mu[1] = 1;
for (int i = 2; i <= 50000; i++)
{
if (!check[i])
{
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt; j++)
{
if (prime[j] * i > 50000) break;
check[prime[j] * i] = 1;
if (i % prime[j] == 0) { mu[prime[j] * i] = 0; break; }
else mu[prime[j] * i] = - mu[i];
}
}
for (int i = 1; i <= 50000; i++) sum[i] = sum[i - 1] + mu[i];
}
ll Cal(int x, int y)
{
ll num = 0;
if (x > y) swap(x, y);
for (int i = 1, last; i <= x; i = last + 1)
{
last = min(x / (x / i), y / (y / i));
num += (ll)(sum[last] - sum[i - 1]) * (x / i) * (y / i);
}
return num;
}
int main()
{
Get_mobius();
scanf("%d", &T);
while (T--)
{
scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
ll ans = Cal(b / k, d / k) - Cal((a - 1) / k, d / k) - Cal(b / k, (c - 1) / k) + Cal((a - 1) / k, (c - 1) / k);
printf("%lld\n", ans);
}
return 0;
}