原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2301
Problem b
Description
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤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≤c≤d≤50000,1≤k≤50000
题解
我们可以很容易就能推出下面的式子:
如果你觉得不是很容易,右转进入ZAP-Queries。
那么我们已经可以求出 1∼a,1∼b 1 ∼ a , 1 ∼ b 的答案了,但题目要求是 a∼b,c∼d a ∼ b , c ∼ d ,我们可以用简单的容斥来解决这个问题:
直接计算 1∼b,1∼d 1 ∼ b , 1 ∼ d 时,我们会多算 1∼a−1,1∼d 1 ∼ a − 1 , 1 ∼ d 和 1∼c−1,1∼b 1 ∼ c − 1 , 1 ∼ b 这一部分,要将其减掉;同时,我们就多减掉了 1∼a−1,1∼b−1 1 ∼ a − 1 , 1 ∼ b − 1 ,把这一部分加回来,我们就得到了正确答案。
代码
#include<bits/stdc++.h>
#define R register int
#define ll long long
using namespace std;
const int M=5e4+5,N=5e4;
int miu[M],p[M],n;
bool check[M];
void getmiu()
{
miu[1]=check[1]=1;
R i,j,t;
for(i=2;i<=N;++i)
{
if(!check[i])p[++p[0]]=i,miu[i]=-1;
for(j=1;j<=p[0];++j)
{
t=i*p[j];
if(t>N)break;
check[t]=1;
if(i%p[j]==0){miu[t]=0;break;}
miu[t]=-miu[i];
}
miu[i]+=miu[i-1];
}
}
ll f(int a,int b,int k)
{
a/=k,b/=k;
if(a>b)swap(a,b);
ll ans=0;R l,r;
for(l=1;l<=a;l=r+1)r=min(a/(a/l),b/(b/l)),ans+=1ll*(miu[r]-miu[l-1])*(a/l)*(b/l);
return ans;
}
void in(){getmiu();scanf("%d",&n);}
void ac(){R a,b,c,d,k,i;for(i=1;i<=n;++i)scanf("%d%d%d%d%d",&a,&b,&c,&d,&k),printf("%lld\n",f(b,d,k)+f(a-1,c-1,k)-f(a-1,d,k)-f(c-1,b,k));}
int main()
{
in();ac();
return 0;
}