题意:给出a b c d k,找到a <= x <= b ,c <= y <=d 使GCD(x,y)=k.
题中说a=c=1;
这个题可以用容斥定理做(如果还没有学习莫比乌斯反演的话,不过时间复杂度要高很多)。
容斥:
k=1时,我们可以看作求互素的数,当k!=1,我们使a b c d k 全部缩小k倍;这样就又转化为求互素的数啦。
此时a=c=1,b=b/k,d=d/k,k=1;
GCD(x,y)和GCD(y,x)是一种情况
i 的范围【a , b】,求 i 在 【c,d 】 范围内互素的数时,会有重复,应求 i 在【 i , d】 范围内互素的数。
(这里要确保d>b)。
时间(780ms)。
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#include<vector>
#define ll long long
ll op[110];
ll s;
void prime(ll n) //求素因数
{
s=0;
for(ll j=2; j*j<=n; j++)
{
if(n%j==0)
{
while(n%j==0)
n/=j;
op[s++]=j;
}
}
if(n!=1)
op[s++]=n;
}
ll nop(ll m) //容斥
{
ll ha[1000]; //素因数及素因数的倍数
ll top=0;
ha[top++]=-1;
for(ll i=0; i<s; i++)
{
ll t=top;
for(ll j=0; j<t; j++)
ha[top++]=ha[j]*op[i]*(-1);
}
ll sum=0;
for(ll i=1; i<top; i++)
sum+=m/ha[i];
return sum;
}
int main()
{
ll t,o=0;
scanf("%lld",&t);
while(t--)
{
o++;
ll a,b,c,d,k;
scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %lld: 0\n",o);
continue;
}
b=b/k;
d=d/k;
if(b>d) //保证d>b
{
int x=b;
b=d;
d=x;
}
ll sum=0;
for(ll i=1; i<=b; i++)
{
prime(i);
sum+=d-nop(d); //d-(i-1)即为范围 [i,d];
sum-=(i-1-nop(i-1));
}
printf("Case %lld: %lld\n",o,sum);
}
}
莫比乌斯反演:
本来这个题是用容斥做的,百度发现大家都用的莫比乌斯,所以现学了。。。
首先 n分解 为素因数之后是这样的形式:
莫比乌斯函数 的值有这样的规律:
1. n=1时
2.1
2.2 其他情况
的(x,y) 的对数。 ( 1<=x<=n,1<=y<=m);
的(x,y) 的对数。 ( 指a是d的倍数)
反演公式:
(写公式好麻烦AAAA
求莫比乌斯函数可用欧拉筛:
int mu[10000]; //记录莫比乌斯函数
void mo(int n)
{
memset(book,0,sizeof(book));
book[1]=1;
mu[1]=1; //n=1时
s=0;
for(int i=2; i<n; i++)
{
if(!book[i])
{
prime[s++]=i;
mu[i]=-1; //i为质数时
}
for(int j=0; j<s&&i*prime[j]<=n; j++)
{
book[i*prime[j]]=1;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0; //次方数不全为1
break;
}
mu[i*prime[j]]=-mu[i];
}
}
}
之后根据反演公式求出 f(1) 的值sum。
去重时,将公式中的n,m换为min(n,m) 求出两个公共部分的 f(1) 的值 ans , ans/2即为多余部分。
sum-ans/2 即为所求值。
时间(30ms)
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#include<vector>
#define ll long long
ll prime[110000];
int mu[110000];
bool book[110000];
ll s;
void mo(int n)
{
book[1]=1;
mu[1]=1;
s=0;
for(int i=2; i<=n; i++)
{
if(!book[i])
{
prime[s++]=i;
mu[i]=-1;
}
for(int j=0; j<s&&i*prime[j]<=n; j++)
{
book[i*prime[j]]=1;
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
}
int main()
{
ll t,o=0;
mo(100000);
scanf("%lld",&t);
while(t--)
{
o++;
ll a,b,c,d,k;
scanf("%lld %lld %lld %lld %lld",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %lld: 0\n",o);
continue;
}
b=b/k;
d=d/k;
ll minn=min(b,d);
ll sum=0,ans=0;
for(ll i=1; i<=minn; i++)
{
sum+=mu[i]*(b/i)*(d/i);
ans+=mu[i]*(minn/i)*(minn/i);
}
sum-=ans/2;
printf("Case %lld: %lld\n",o,sum);
}
}