题目描述
对于给出的nnn个询问,每次求有多少个数对(x,y)(x,y)(x,y),满足a≤x≤ba≤x≤ba≤x≤b,c≤y≤dc≤y≤dc≤y≤d,且gcd(x,y)=kgcd(x,y) = kgcd(x,y)=k,gcd(x,y)gcd(x,y)gcd(x,y)函数为xxx和yyy的最大公约数。
题解
先求1−x1-x1−x,1−y1-y1−y的满足gcd(x,y) = kgcd(x,y)\ =\ kgcd(x,y) = k的数对:我们设f(i)f(i)f(i)表示k∣gcd(x,y)k|gcd(x,y)k∣gcd(x,y)的方案书,g(i)g(i)g(i)表示gcd(x,y)=kgcd(x,y)=kgcd(x,y)=k的方案数。
根据莫比乌斯反演公式,则一定有:
f(i) = ∑d=1⌊ni⌋ g(i∗d)  ⟺  g(i) = ∑d=1⌊ni⌋μ(d)∗f(i∗d)f(i)\ =\ \sum_{d=1}^{\lfloor \frac{n}{i}\rfloor}\ g(i*d)\iff g(i)\ =\ \sum_{d=1}^{\lfloor \frac{n}{i}\rfloor}μ(d)*f(i*d)f(i) = d=1∑⌊in⌋ g(i∗d)⟺g(i) = d=1∑⌊in⌋μ(d)∗f(i∗d)
因此对于前者μ(d),我们可以使用前缀和来进行求解。对于后者,我们考虑除法分块来实现。
根据f数组的定义,我们可以知道f(i∗d) =⌊ni∗d⌋⌊mi∗d⌋f(i*d)\ =\lfloor \frac{n}{i*d}\rfloor \lfloor \frac{m}{i*d}\rfloorf(i∗d) =⌊i∗dn⌋⌊i∗dm⌋.
因此我们需要利用除法分块来求解f(i∗d)f(i*d)f(i∗d).显然对于⌊ni∗d⌋\lfloor \frac{n}{i*d}\rfloor⌊i∗dn⌋和对于⌊mi∗d⌋\lfloor \frac{m}{i*d}\rfloor⌊i∗dm⌋会形成不同的序列,每次对于i求解两个右端点中较小点=j=j=j即可。
设ans(x,y)ans(x,y)ans(x,y)表示1−x,1−y1-x,1-y1−x,1−y的答案。
现在考虑原题,由于有范围限制,根据容斥原理就很容易就想到:
ans = ans(b,d)−ans(a−1,d)−ans(b,c−1)+ans(a−1,c−1)ans\ =\ ans(b,d)-ans(a-1,d)-ans(b,c-1)+ans(a-1,c-1)ans = ans(b,d)−ans(a−1,d)−ans(b,c−1)+ans(a−1,c−1)
然后代码就很好实现了:
#include <bits/stdc++.h>
using namespace std;
const int N = 100000;
int Miu[N+10];
int vis[N+10];
int sum[N+10];
int prime[N+10];
void Find_Miu(void)
{
int m = 0;
Miu[1] = 1;
for (int i=2;i<=N;++i)
{
if (vis[i] == 0) prime[++m] = i, Miu[i] = -1;
for (int j=1;j<=m && i*prime[j]<=N;++j)
{
vis[i*prime[j]] = 1;
if (i%prime[j] == 0)
{
Miu[i*prime[j]] = 0;
break;
}
Miu[i*prime[j]] = -Miu[i];
}
}
return;
}
void Get_sum(void)
{
for (int i=1;i<=N;++i)
sum[i] = sum[i-1]+Miu[i];
return;
}
long long get(int a,int b,int d)
{
int j,n,m;
n = a/d;
m = b/d;
long long ans = 0;
for (int i=1;i<=min(n,m);i=j+1)//不要写成i++
{
j = min(n/(n/i),m/(m/i));
ans += (long long)(sum[j]-sum[i-1])*(n/i)*(m/i);
}
return ans;
}
void work(void)
{
int a,b,c,d,k;
scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
printf("%lld\n",get(b,d,k)-get(a-1,d,k)-get(b,c-1,k)+get(a-1,c-1,k));
return;
}
int main(void)
{
int n;
Find_Miu();
Get_sum();
scanf("%d",&n);
while (n -- ) work();
return 0;
}