题目大意:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
题解:
令a′=[a/d],b′=[b/d]
∑i=1a∑j=1b[gcd(i,j)=k]
=
由
μ∗I=e
=
又由d|gcd(x,y)⇔d|x & d|y
=
由高斯记号的性质,易知[a′d]随d的增大单调不增,且最多有2a′−−√种取值,按照权值分块即可
具体分块的方法类似1257,但这个是两个区间,要麻烦一些
2301的话加一个容斥即可
我的收获:反演神啊
2301
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=50005;
int a,b,c,d,k;
int p,T,pri[M],u[M],sum[M];
bool f[M];
void sieve()
{
sum[1]=u[1]=1;
for(int i=2;i<=M;i++){
if(!f[i]) pri[++p]=i,u[i]=-1;
for(int j=1;j<=p&&i*pri[j]<=M;j++){
f[i*pri[j]]=1;
if(i%pri[j]==0){u[i*pri[j]]=0;break;}
u[i*pri[j]]=-u[i];
}
sum[i]=sum[i-1]+u[i];
}
}
int calc(int n,int m){
int ret=0;n/=k;m/=k;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));//因为两个区间要取相交,所以r要取min
ret+=(n/l)*(m/l)*(sum[r]-sum[l-1]);
}
return ret;
}
void init()
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d\n",calc(b,d)-calc(a-1,d)-calc(c-1,b)+calc(a-1,c-1));
}
int main()
{
sieve();
cin>>T;
while(T--) init();
return 0;
}