标签:数学
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
a,b,c,d,k<=50000
记g(a,b,c,d)为题目所求,记f(m,n)为满足1≤x≤m,1≤y≤n,且gcd(x,y) = k的数对个数,记F(m,n)为满足1≤x≤m,1≤y≤n,且gcd(x,y)%k=0的数对个数。
首先我们不难发现,
g(a,b,c,d)=f(b,d)−f(a−1,d)−f(b,c−1)+f(a−1,c−1)
,而f(n,m)与F(n,m)满足莫比乌斯反演。
显然地,
F(n,m)=[n/k]∗[m/k]
则有
f(n,m)=∑min(n/k,m/k)i=1F(n/i,m/i)∗μ(i)
但这样一来,复杂度还是
O(n2)
,无法满足要求
注意到
n/i
只有
n√
种取值,我们预处理
μ(i)
的前缀和即可
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define N 50050
using namespace std;
typedef long long ll;
int u[N],vis[N],p[N],top=0,t,a,b,c,d,k;
ll ans;
ll solve(int n,int m)
{
n/=k,m/=k;
ll ret=0,last=0;
if (n>m) swap(n,m);
for (int i=1;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ret+=(ll)(n/i)*(m/i)*(u[last]-u[i-1]);
}
return ret;
}
int main()
{
scanf("%d",&t),memset(vis,0,sizeof(vis)),u[1]=1;
for (int i=2;i<=N-50;i++)
{
if (!vis[i]) {p[++top]=i,u[i]=-1;}
for (int j=1;j<=top&&i*p[j]<=N-50;j++)
{
vis[i*p[j]]=1;
if (i%p[j]) u[i*p[j]]=u[i]*u[p[j]];else break;
}
}
for (int i=2;i<=N-50;i++) u[i]+=u[i-1];
while (t--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k),ans=solve(b,d)-solve(a-1,d)-solve(c-1,b)+solve(a-1,c-1);
printf("%lld\n",ans);
}
}