GCD
Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.
Yoiu can assume that a = c = 1 in all test cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.
2 1 3 1 5 1 1 11014 1 14409 9
Case 1: 9 Case 2: 736427
题意:给出5个整数:a,b,c,d,k,在区间[a,b]内选取一个数 x, 在[c,d]内选取一个数 y,使得GCD(x,y)=k。其中GCD(x,y)表示x和y的最大公约数。求这样的整数共有多少对。注意(x=5,y=7和x=7 y=5)是一样的。
本题中的数据a=c=1;
分析:
(1) 由于gcd(x,y)=k,满足 gcd(x/k,y/k)=1,所以区间可以缩小为 [1/k,b/k],[1/k,d/k], 问题也就转化为取两区间中的元素 x, y,使得 gcd(x,y)=1;
(2) 由于gcd(x,y)=1与gcd(y,x)=1是相同的,假设x<y,于是可以先取小区间进行讨论, 由于 gcd(x,y)=1,利用欧拉函数,所以小区间可以计算为
ans+=phi[1]+phi[2]+phi[3]+.............+phi[b](用欧拉函数的递推形式保存起来即可);
(3)对于[b/k+1 , d/k ],进行讨论,设y为这个区间里的一个元素,可以对y进行素因子分解,于是得到集合 : {p1,p2,p3........},先求gcd(x,y)!=1的组合,用容斥原理讨论能被整除的个数,最后相减就能得到ans了。
AC代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=1e5+5;
int nsu[1000];///每个数的素因子
int no; ///每个数的素因子的数量
int b,d,sum;
void dfs(int i,int nu,int x,int mu)///i表示要乘以prime数组的下标。
{ ///nu表示已经相乘的素因子的数量
if(nu==x) ///x表示上限,最多有x个素因子相乘
{ ///已经相乘的素因子的值
sum+=b/mu;
return ;
}
if(i==no)
return ;
dfs(i+1,nu+1,x,mu*nsu[i]);
dfs(i+1,nu,x,mu);
return ;
}
int rong()
{
int s=0;
for(int i=1;i<=no;i++)
{
sum=0;
dfs(0,0,i,1);
if(i&1)///容斥原理,奇加偶减
s+=sum;
else
s-=sum;
}
return b-s;
}
int main()
{
int phi[maxn];
int prime[maxn];///存储到maxn的所有素数
int nprime=0;
bool isprime[maxn];
///欧拉函数的递推形式
for(int i=1;i<maxn;i++) phi[i]=i;
for(int i=2;i<maxn;i+=2) phi[i]/=2;
for(int i=3;i<maxn;i+=2)
if(phi[i]==i)
{
for(int j=i;j<maxn;j+=i)
phi[j]=phi[j]/i*(i-1);
}
///线性筛素数
memset(isprime,true,sizeof(isprime));
isprime[0]=isprime[1]=false;
for(long long i=2;i<=maxn;i++)
{
if(isprime[i])
{
prime[nprime++]=i;
for(long long j=i*i;j<maxn;j+=i)
isprime[j]=false;
}
}
int t;
int a,k,ca=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&a,&b,&a,&d,&k);
if(k==0)
{
printf("Case %d: 0\n",++ca);
continue;
}
b/=k;d/=k;
if(b>d)
{
int temp=b;b=d;d=temp;
}
long long ans=0;
for(int i=1;i<=b;i++)
ans+=phi[i];
for(int i=b+1;i<=d;i++)
{
no=0; ///no为a的素因子的个数
a=i;
for(int j=0;j<nprime&&prime[j]*prime[j]<=a;j++)///对大于b小于等于d的数进行素因子的分解
if(a%prime[j]==0)
{
nsu[no++]=prime[j];
while(a%prime[j]==0)
a/=prime[j];
}
if(a>1)
nsu[no++]=a;
ans+=rong();
}
printf("Case %d: %I64d\n",++ca,ans);
}
return 0;
}