题目链接:https://vjudge.net/problem/HDU-1695
转自:https://blog.csdn.net/qq_41129854/article/details/84768431
题意:求1~ b与1 ~ d之间gcd=k的数对数。两数互换视为同一组。
思路:求(1,b)与(1,d)之间gcd=k的对数等于求区间1:(1,b/ k)与区间2:(1,d/ k)之间互素的数的个数。 可以用容斥原理求出区间1中与区间2中互素的对数( 一个数a除以b的商就是小于等于a内有多少个数是b的倍数 )。但注意要去重。,例如n=3,m=5,重复是(1,2)和(2,1),(1,3)和(3,1),(2,3)和(3,2),发现重复的就是(2,1),(3,1),(3,2),即2-n的各个数的欧拉函数和 ,这时候就可以用欧拉函数算出重复的部分。
由于phi[1]=1,因此需要额外修正。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
long long phi[maxn];
void phi_table()//欧拉函数
{
memset(phi,0,sizeof(phi));
phi[1]=1;
for(int i=2; i<=1e5; i++)
{
if(!phi[i])
{
for(int j=i; j<=1e5; j+=i)
{
if(!phi[j])
{
phi[j]=j;
}
phi[j]=phi[j]/i*(i-1);
}
}
}
}
vector <long long >v;
void prime(long long n)//因数分解
{
v.clear();
for(long long i=2;i*i<=n;i++)
{
if(n%i==0)
{
v.push_back(i);
while(n%i==0)
n/=i;
}
if(n==1)
break;
}
if(n>1)
v.push_back(n);
}
long long fg(long long n)//容斥原理求互素的对数
{
long long sz=v.size();
long long sum=0;
for(long long i=1;i<(1<<sz);i++)
{
long long ans=1,cnt=0;
for(long long j=0;j<sz;j++)
{
if(i&(1<<j))
{
ans*=v[j];
cnt++;
}
}
if(cnt%2==1)
{
sum=sum+n/ans;
}
else
sum=sum-n/ans;
}
return n-sum;
}
int main()
{
int T,kase=0;
long long a,b,c,d,k;
long long n,m;
scanf("%d",&T);
phi_table();
while(T--)
{
scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
printf("Case %d: ",++kase);
if(k==0||k>b||k>d)
{
printf("0\n");
continue;
}
n=b/k,m=d/k;
if(n>m)
swap(n,m);
long long sum=1;//phi[1]=1的修正
for(long long i=1;i<=n;i++)
{
prime(i);
sum+=fg(m);
sum-=phi[i];
}
printf("%lld\n",sum);
}
return 0;
}