题目链接:bzoj2301
题目大意:
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
题解:
分块+莫比乌斯反演
原式即
∑a≤x≤b∑c≤y≤d∑gcd(x,y)=k1
同样的,化式子:
∑ak≤xk≤bk∑ck≤yk≤dk∑gcd(xk,yk)=11
设 xk 为 i ,
于是式子就变成了:
∑1≤i≤n∑1≤j≤m∑gcd(i,j)=11
即
∑1≤i≤n∑1≤j≤mϵ(gcd(i,j))
因为有 1∗μ=ϵ ,即 ∑d|nμ(d)=ϵ(n)
所以式子变成:
∑1≤i≤n∑1≤j≤m∑d|(i,j)μ(d)
即
∑1≤i≤n∑1≤j≤m∑d|i,d|jμ(d)
于是套路变形:
∑1≤d≤min(n,m)μ(d)×⌊nd⌋×⌊md⌋
然后就可以做了。
还是套路..对
⌊id⌋×⌊jd⌋
相等的进行分块,每次直接计算这段区间的和。但是因为
d
是会变的,所以计算完
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 50100
bool ispri[maxn];
int k,cnt,pri[maxn],sum[maxn],mu[maxn];
int mymin(int x,int y){return (x<y)?x:y;}
void mobius(int lim)
{
cnt=0;sum[0]=0;mu[1]=1;
for (int i=2;i<=lim;i++)
{
if (!ispri[i])
{
pri[++cnt]=i;
mu[i]=-1;
}
for (int j=1;j<=cnt && pri[j]*i<=lim;j++)
{
ispri[pri[j]*i]=true;
if (i%pri[j]==0)
{
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
}
}
for (int i=1;i<=50000;i++) sum[i]=sum[i-1]+mu[i];
}
int get(int n,int m)
{
int lim,ret,x,y,lx,ly,r;
ret=0;n/=k;m/=k;
lim=mymin(n,m);
for (int i=1;i<=lim;i=r+1)
{
x=n/i;y=m/i;
lx=mymin(lim,n/x);
ly=mymin(lim,m/y);
r=mymin(lx,ly);
ret+=(sum[r]-sum[i-1])*x*y;
}
return ret;
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int T,a,b,c,d;
scanf("%d",&T);
mobius(50000);
while (T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
int ans=get(b,d)-get(a-1,d)-get(b,c-1)+get(a-1,c-1);
printf("%d\n",ans);
}
return 0;
}