hdu 1695 GCD(莫比乌斯反演)

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
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;



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

一、

二、


#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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值