Description
对于给出的(n)个询问,每次求有多少个数对((x,y)),满足(a le x le b,c le y le d),且(gcd(x,y) = k),(gcd(x,y))函数为(x)和(y)的最大公约数。
Input
第一行一个整数n,接下来n行每行五个整数,分别表示(a,b,c,d,k)。
Output
共(n)行,每行一个整数表示满足要求的数对((x,y))的个数。
Sample Input
2
2 5 1 5 1
1 5 1 5 2
Sample Output
14
3
HINT
(100\%)的数据满足:(1≤n≤50000,1≤a≤b≤50000,1 le c le d≤50000,1 le k le 50000)
板子题。
首先我们学会求$$sum_{i=1}^{N} sum_{j=1}^{M}[gcd(i,j) = k]$$
那么题目所求就可以用容斥原理来解决了。
这个式子可以化为$$sum_{i=1}^{N} sum_{j=1}^{M}[gcd(lfloor frac{i}{k}
floor,lfloor frac{j}{k}
floor) = 1] = sum_{i=1}^{lfloor frac{N}{k}
floor} sum_{j=1}^{lfloor frac{M}{k}
floor}[gcd(i,j)=1]$$
利用莫比乌斯反演$$sum_{i mid N} mu(i) = [N = 1]$$
那么式子就可以化简为$$sum_{i=1}^{lfloor frac{N}{k}
floor}sum_{j=1}^{lfloor frac{M}{k}
floor}sum_{t mid i land t mid j} mu(t)$$
先枚举(t),就可以得到$$sum_{t=1}^{min(lfloor frac{N}{k}
floor,lfloor frac{M}{k}
floor)}mu(t)lfloor frac{lfloor frac{N}{k}
floor}{t}
floor lfloor frac{lfloor frac{M}{k}
floor}{t}
floor$$
然后就可以计算(mu)的前缀和,进行(sqrt{N})分段了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define maxn 100010
int mu[maxn],prime[maxn],tot; bool exist[maxn];
inline void ready()
{
mu[1] = 1;
for (int i = 2;i <= 50000;++i)
{
if (!exist[i]) prime[++tot] = i,mu[i] = -1;
for (int j = 1;j <= tot&&i*prime[j] <= 50000;++j)
{
exist[i*prime[j]] = true;
if (i % prime[j] == 0) { mu[i*prime[j]] = 0; break; }
mu[i*prime[j]] = -mu[i];
}
}
for (int i = 1;i <= 50000;++i) mu[i] += mu[i-1];
}
inline int work(int a,int b,int k)
{
a /= k; b /= k;
if (a > b) swap(a,b);
int ret = 0,pos;
for (int i = 1;i <= a;i = pos+1)
{
pos = min(a/(a/i),b/(b/i));
ret += (mu[pos]-mu[i-1])*(a/i)*(b/i);
}
return ret;
}
int main()
{
freopen("2301.in","r",stdin);
freopen("2301.out","w",stdout);
ready();
int T; scanf("%d",&T);
while (T--)
{
int a,b,c,d,k,ans;
scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
ans = work(a-1,c-1,k)+work(b,d,k)-work(a-1,d,k)-work(b,c-1,k);
printf("%d
",ans);
}
fclose(stdin); fclose(stdout);
return 0;
}