hdu 1695 GCD(莫比乌斯反演)

时间:2021-06-04 05:17:56

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
 
  
21 3 1 5 11 11014 1 14409 9
 

Sample Output
 
  
Case 1: 9Case 2: 736427
Hint
For the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).
 

Source


题意:给你a,b,c,d,k,让你求有多少对x,y(a <= x <= b, c <= y <= d)满足gcd(x,y) == k,gcd(x,y)和gcd(y,x)算一个。(保证每组数据a = c = 1)


转换一下,因为要求gcd(x,y) = k,也就可以看成 求gcd(x/k,y/k) = 1 有多少对。

我们设一个函数F(n) 为gcd是n的倍数的对数有多少对(这里计算的对数是同时包含gcd(x,y)和gcd(y,x)的)。

设函数f(n)为gcd为n的对数有多少,

可以发现F(n) = sigma(f(d))   ( n|d ),而且F(n)这个函数的值比较好求,就是(b/n)*(d/n),因为区间[1,b],[1,d]内n的倍数的个数就是 b/n 和 d/n,显然两个n的倍数的gcd肯定是n的倍数。

根据上面的转换,我们要求的是gcd为1的个数,也就是f(1),这样我们就可以利用莫比乌斯反演快速的求得了。


当然,我们求得的答案里面是有重复的,也就是gcd(x,y),gcd(y,x),假设b < d,

设我们求出  [1,b] 与 [1,d] 这两个区间的答案为 sum1,

因为 [1,b] 与 [b+1,d] 这两个区间内是没有相同的数的,所以这段的答案是没有重复的,

而[1,b] 与 [1,b]这段是有重复的,设这段求出的值为sum2,那么重复的次数就为sum2 / 2,

所以最终的答案就是sum1 - sum2 / 2;



下面是莫比乌斯反演的两种形式,这题我们利用第二种:

一、hdu 1695 GCD(莫比乌斯反演)

二、hdu 1695 GCD(莫比乌斯反演)


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;

const int maxn = 1e5 + 10;
int p[maxn/10];
int flag[maxn];
int mu[maxn];
int cnt = 0;
void init()
{
int i,j;
mu[1] = 1;
for(i=2;i<maxn;i++)
{
if(!flag[i])
{
p[cnt++] = i;
mu[i] = -1;
}
for(j=0;j<cnt&&p[j]*i<maxn;j++)
{
flag[p[j]*i] = 1;
if(i % p[j] == 0)
{
mu[p[j]*i] = 0;
break;
}
mu[p[j]*i] = -mu[i];
}
}
}

int main(void)
{
int T,a,b,c,d,k,i,j;
init();
scanf("%d",&T);
int cas = 1;
while(T--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k == 0)
{
printf("Case %d: 0\n",cas++);
continue;
}
if(b > d)
swap(b,d);
b = b/k;
d = d/k;
LL sum1 = 0;
LL sum2 = 0;
for(i=1;i<=b;i++)
{
sum1 += (LL)mu[i]*(b/i)*(d/i);
}
for(i=1;i<=b;i++)
{
sum2 += (LL)mu[i]*(b/i)*(b/i);
}
LL ans = sum1 - sum2/2;
printf("Case %d: %lld\n",cas++,ans);
}
return 0;
}