HDU 1695 GCD 欧拉函数+容斥定理 || 莫比乌斯反演

时间:2024-01-05 14:17:20

GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4141    Accepted Submission(s): 1441

Problem Description
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.

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 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.
Output
For each test case, print the number of choices. Use the format in the example.
Sample Input
2
1 3 1 5 1
1 11014 1 14409 9
Sample Output
Case 1: 9
Case 2: 736427
 /*
题意:区间x属于[1,A] , y属于区间[1,B]
求最大公约数是K,即gcd(x,y)=K。
并且[1,3]和[3,1]属于同一种情况。 思路:HDU 4135 Co-prime 的思路在这一题有用。
它的题意:区间[A,B],与整数N的互素的个数
对于这一到题目:gcd(x,y)=k.
要满足最大公约数是K,可以转化为
[1,A],[1,B]==>[1,A/K],[1,B/K] 求互素的个数。
好像有点难以想到。????
{{
借鉴一下别人是说法。会更明白
gcd(x, y) == k 说明x,y都能被k整除,但是能被k整除的未必gcd=k ,
必须还要满足互质关系.
问题就转化为了求1~a/k 和 1~b/k间互质对数的问题
}}
这样的话,如何处理呢?
题意要求[1,3]和[3,1]不能重复。
对于区间[1,A/K],[1,B/K] 看成==>[1,a],[1,b] 有几种情况
1____________a
1____________________b 1____________a
1________b 1____________a
1____________b 这三种情况。我们来个判断,总是让a<=b,用b做更大的值。就会变成 1—————————a
1—————————————————b
在求取的过程中也是采取这样的规则。
[?,b1];确定后一位数。表示在[1,a]中与b1互质的个数。
那么就很好的避免了[1,3],[3,1]的情况了。
求取总和sum=sum1+sum2;
sum1=欧拉函数值[1,a]; 想想为什么?
sum2={枚举a+1--->b,与区间[1,a]互质的个数};
sum2就和以前的一题有关系了,要用欧拉函数+容斥定理处理。
具体的参考:http://www.cnblogs.com/tom987690183/p/3246197.html */ #include<stdio.h>
#include<string.h>
#include<stdlib.h> int prime[],len;
bool s[];
int opl[];
int Que[];
int f[],flen; void make_prime() //素数打表
{
int i,j;
len=;
for(i=;i<=;i++)//刚开始写错,i*i<=100000;⊙﹏⊙b汗
if(s[i]==false)
{
prime[++len]=i;
for(j=i*;j<=;j=j+i)
s[j]=true;
}
} void make_Euler() //欧拉函数打表。
{
int i,j;
make_prime();
for(i=;i<=;i++)
opl[i]=i;
opl[]=;
for(i=;i<=len;i++)
for(j=prime[i];j<=;j=j+prime[i])
opl[j]=opl[j]/prime[i]*(prime[i]-);
} void make_dEuler(int n) //单点欧拉的素因子。
{
int i;
flen=;
for(i=;i*i<=n;i++)
{
if(n%i==)
{
while(n%i==)
n=n/i;
f[++flen]=i;
}
}
if(n!=)
f[++flen]=n;
} int Capacity(int m)
{
int i,j,t=,sum=,k;
Que[t++]=-;
for(i=;i<=flen;i++)
{
k=t;
for(j=;j<k;j++)
Que[t++]=-*Que[j]*f[i];
}
for(i=;i<t;i++)
sum=sum+m/Que[i];
return sum;
} void sc()//输出函数,测试用的。
{
int i;
for(i=;i<=;i++)
printf("%d ",opl[i]);
printf("\n");
} __int64 make_ini(int b,int c,int k)
{
int i,x,y,tmp;
__int64 sum=;
x=b/k;y=c/k;//加特判的用处。不能除0
if(x>y)
{
tmp=x;
x=y;
y=tmp;
}
for(i=;i<=x;i++)
sum=sum+opl[i];//第一步
for(i=x+;i<=y;i++)//第二步,枚举
{
make_dEuler(i);
sum=sum+(x-Capacity(x));
}
//sc();
return sum; } int main()
{
int T,a,b,c,d,k,i;
__int64 sum;
make_Euler();
while(scanf("%d",&T)>)
{
for(i=;i<=T;i++)
{
sum=;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==) //特判,否则会Runtime Error (INTEGER_DIVIDE_BY_ZERO)
{
sum=;
}
else sum=make_ini(b,d,k);
printf("Case %d: %I64d\n",i,sum); }
}
return ;
}
下面再介绍一种方法。莫比乌斯反演
GCD(a,b) = d;
可以转化为
GCD(a/d,b/d) = 1;
设f(d)为(a,b) = d的种类数
   F(d)为(a,b) = d 的倍数 的种类数。
例如
F(2) = (a/2)*(b/2);
F(3) = (a/3)*(b/3);
HDU 1695 GCD 欧拉函数+容斥定理 || 莫比乌斯反演
mu[i]可以打表求出。
关于一个优化在于a/i = a/(i+k) && b/i = b/(i+k);
此时我们能节省时间来求。详见代码部分
 #include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
using namespace std;
typedef __int64 LL; const int maxn = 1e5+;
bool s[maxn];
int prime[maxn],len = ;
int mu[maxn];
int sum1[maxn];
void init()
{
memset(s,true,sizeof(s));
mu[] = ;
for(int i=;i<maxn;i++)
{
if(s[i] == true)
{
prime[++len] = i;
mu[i] = -;
}
for(int j=;j<=len && (long long)prime[j]*i<maxn;j++)
{
s[i*prime[j]] = false;
if(i%prime[j]!=)
mu[i*prime[j]] = -mu[i];
else
{
mu[i*prime[j]] = ;
break;
}
}
}
for(int i=;i<maxn;i++)
sum1[i] = sum1[i-]+mu[i];
}
LL solve(int a,int b)
{
LL sum = ;
for(int i=,la = ;i<=a;i++,i = la+)
{
la = min(a/(a/i),b/(b/i)); //优化部分
sum = sum + ((LL)(a/i))*(b/i)*(sum1[la]-sum1[i-]);
}
return sum;
}
int main()
{
int T,l,a,b,d;
init();
scanf("%d",&T);
for(int t=;t<=T;t++)
{
scanf("%d%d%d%d%d",&l,&a,&l,&b,&d);
LL sum = ;
if(d==) ;
else{
if(a>b) swap(a,b);
sum = solve(a/d,b/d);
sum = sum - solve(a/d,a/d)/;
}
printf("Case %d: %I64d\n",t,sum);
}
return ;
}