题意:给出a、b、c、d、k,求在[a,b]内取一个数x,在[c,d]内取一个数y,使得gcd(x,y)=k的方法数
思路:可以说是hdu1695的升级版了,相当于在[a/k,b/k]中取一个x,在[c/k,d/k]中取取一个y,使得gcd(x,y)=1的方法数,定义一个solve函数,作用是求[1,m]内取x,[1,n]内取y,使得gcd(x,y)=1的方法数,根据hdu1695的思路,则f(1)=F(1)*mu(1)+...+F(min(x,y))*mu(min(x,y)),但如果还是这样做会超时,所以要用分块优化,贴一下分块优化的代码,其中的sum是莫比乌斯函数的前缀和:
int solve(int m,int n)
{
int last;
int ans=0;
for(int i=1;i<=min(m,n);i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(sum[last]-sum[i-1]);
}
return ans;
}
然后再用一下容斥,最后的结果为
ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve((c-1)/k,b/k)+solve((a-1)k,(c-1)/k)
因为我们需要求得[a,b]和[c,d]这两个区间,仔细一想就明白了
还有一点这题必须要用int,用long long会WA,非常不解(??????)
完整代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL ;
const int N=50005;
bool check[N+5];
int prime[N+5];
int mu[N+5];
int sum[N+5];
int tot;
void miu()
{
memset(check,false,sizeof(check));
mu[1]=1;
tot=0;
for(int i=2;i<=N;i++)
{
if(!check[i])
{
prime[tot++]=i;
mu[i]=-1;
}
for(int j=0;j<tot;j++)
{
if(i*prime[j]>N) break;
check[i*prime[j]]=true;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
else
mu[i*prime[j]]=-mu[i];
}
}
sum[0]=0;
for(int i=1;i<=N;i++)
sum[i]=sum[i-1]+mu[i];
}
int solve(int m,int n)
{
int last;
int ans=0;
for(int i=1;i<=min(m,n);i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(sum[last]-sum[i-1]);
}
return ans;
}
int main()
{
miu();
int t;
scanf("%d",&t);
while(t--)
{
int a,b,c,d,k;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
int ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-
solve((c-1)/k,b/k)+solve((a-1)/k,(c-1)/k);
printf("%d\n",ans);
}
return 0;
}