题目大意:
给你a, b, c, d, k; 找出这样的一队 x, y, 使得 gcd(x , y) = k, 并且x ∈[1, b], y ∈ [1, d], 问有多少对符合要求的(x, y)。
思路: gcd(x, y) == k 说明x,y都能被k整除, 但是能被k整除的未必gcd=k , 必须还要满足互质关系. 问题就转化为了求1~a/k 和 1~b/k间互质对数的问题可以把a设置为小的那个数, 那么以y>x来保持唯一性(题目要求, 比如[1,3] =[3,1] ) 接下来份两种情况:
- y <= a , 那么对数就是 1~a的欧拉函数的累计和(容易想到)
- y >= a , 这个时候欧拉函数不能用了,怎么做? 可以用容斥原理,把y与1~a互质对数问题转换为y的质数因子在1~a范围内能整除的个数(质数分解和容斥关系),
#include <iostream>
#include <stdio.h>
#include <memory.h>
#include<math.h>
#include <vector>
using namespace std;
const int N = 100005;
typedef long long LL;
#define maxn 100005
LL phi[N];
vector<LL> link[N];
int vis[1000000+5],c;
LL prime[79000];
void get_prime() //打印素数表模板
{
int i,j,n,m;
c=0;
n=1000000;
m=(int)sqrt(n+0.5);
memset(vis,0,sizeof(vis));
for(i=2;i<=m;i++)
if(!vis[i])
{
for(j=i*i;j<=n;j+=i)
vis[j]=1;
}
for(i=2;i<=n;i++) if(!vis[i])
prime[c++]=i;
}
void get_PHI() //模板 得到1->n-1 与n互质的数的个数 存入phi[n] (欧拉函数打表)
{
int i,j;
for (i = 1; i <= maxn; i++) phi[i] = i;
for (i = 2; i <= maxn; i += 2) phi[i] /= 2;
for (i = 3; i <= maxn; i += 2) if(phi[i] == i)
{
for (j = i; j <= maxn; j += i)
phi[j] = phi[j] / i * (i - 1);
}
}
int ouler(int x) //欧拉函数:判断得到1->n-1 与n互质的数的个数 (不打表)
{
int i,j,k=1;
int m=x;
for(i=2;i<=m;i++)
{
if(m%i==0)
{
k=k*(i-1);
while(m%i==0)
{
m=m/i;
k*=i;
}
k/=i;
}
}
return k;
}
void init() //求每一个数的质因数,vector储存
{
LL i, j, k;
for(i = 1; i < N; i++)//求n的质因数 也是模板
{
k = i;
for(j = 0; prime[j]*prime[j] <= k; j++)
{
if(k%prime[j] == 0)
{
link[i].push_back(prime[j]);
while(k%prime[j] == 0)
k /= prime[j];
}
if(k == 1) break;
}
if(k > 1) link[i].push_back(k);
}
}
LL make_ans(LL num,LL n)//1到num中的所有数与n的m个质因子不互质的数的个数 注意是不互质哦 容斥原理
{
LL ans=0,tmp,i,j,flag;
for(i=1;i<(LL)(1<<link[n].size());i++)
{ //用二进制来1,0来表示第几个素因子是否被用到,如m=3,三个因子是2,3,5,则i=3时二进制是011,表示第2、3个因子被用到
tmp=1,flag=0;
for(j=0;j<link[n].size();j++)
if(i&((LL)(1<<j)))//判断第几个因子目前被用到
flag++,tmp*=link[n][j];//第j个质因子link[n][j]
if(flag&1)//容斥原理,奇加偶减
ans+=num/tmp;
else
ans-=num/tmp;
}
return ans;
}
int main()
{
LL i, a, b, c, d, k, sum, t, zz = 1;//longlong型的数据 可以用%I64d 来输入输出
get_prime();
get_PHI();
init();
scanf("%I64d", &t);
while(t--)
{
scanf("%I64d %I64d %I64d %I64d %I64d", &a, &b, &c, &d, &k);
if(k == 0 || k > b || k > d)
{
printf("Case %I64d: 0\n", zz++);
continue;
}
if(b > d) swap(b, d);//保持d较大
b /= k;
d /= k;
sum = 0;
for(i = 1; i <= b; i++)
{
sum += phi[i];
}
for(i = b+1; i <= d; i++)
{
sum += b - make_ans(b, i);
}
printf("Case %I64d: %I64d\n", zz++, sum);
}
return 0;
}