HDU 1695:GCD(莫比乌斯反演)

给你 a , b , c , d , k 五个值 (题目说明了 你可以认为 a=c=1) ,所以 x 属于 [1,b] ,y属于[1,d] 让你求有多少对这样的 (x,y)满足gcd(x,y)==k。

首先 ,这道题可以进行简化,因为 gcd(x,y)=k, 那么,很显然 gcd(x / k,y / k)是等于 1 的。那么,此时问题就可以转化为: x 属于 [1,b / k] ,y属于[1,d / k] ,让你求有多少对这样的 (x,y)满足gcd(x,y)== 1 ,即x和y是互质的。 我们需要用到莫比乌斯反演(当然容斥定理也可以),先来看一下什么是莫比乌斯反演。

这里先给出莫比乌斯的两个公式 :
这里写图片描述
这里写图片描述

这两个就是莫比乌斯反演的两种表现形式
反演的核心所在是莫比乌斯函数,什么是莫比乌斯函数呢? 我们在下面给出它的定义:

这里写图片描述

它就是莫比乌斯函数,我们称之为 mu 函数,也是整个反演最为重要的部分。

现在我们来继续解决上面的那个问题。如何去求有多少对这样的 (x,y)满足gcd(x,y)== 1 。这个问题你直接拿到手发现确实比较麻烦,但是换个思路,如果我们去求有多少对这样的 (x,y)满足 gcd(x,y)== 1 的倍数 呢? 是不是就非常简单了呢?

我们设:

F[i] 为有多少对(x,y)满足 gcd(x,y)== i 的倍数
f[i] 为有多少对(x,y)满足 gcd(x,y)== i

F[i] 很容易求出是 F[i]=b/i*d/i

所以我们需要解决的问题,就是如何求出 f[1]

扫描二维码关注公众号,回复: 2606082 查看本文章

根据这里写图片描述公式你可以发现,在你对函数进行构造时是需要满足反演对函数的要求的,这个需要你自己来体会,至于另一个公式的构造方法是 “约数” 的关系,而这个则是 “倍数” 的关系。
我们很容易得到 F[i]=f[i] + f[i*2] + f[i*3]+…..
根据公式我们也很容易得到 f[1]= mu[1]*F[1]+ mu[2]*F[2]+……

我们在对[1,b/k]和[1,d/k]这两个区间求有多少对(x,y)满足gcd(x,y)=1的时候,实际上有重复的情况
因为(x,y)是无序的,在[1,min(b/k,d/k)]区间上,我们实际上计算了两次,所以我们还需要减去多的一次,具体看代码。
还有就是 k 可能为0,记得特判一下
代码:

#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;
const int maxn=100000+100;

int mu[maxn];
bool isprime[maxn];
int prime[maxn],tot;

void init(){

    for(int i=0;i<maxn;i++) isprime[i]=true;
    isprime[0]=isprime[1]=false;
    tot=0;
    mu[1]=1;
    for(int i=2;i<maxn;i++){

        if(isprime[i]) prime[tot++]=i,mu[i]=-1;
        for(int j=0;j<tot && i*prime[j]<maxn;j++){

            isprime[i*prime[j]]=false;
            if(i%prime[j]) mu[i*prime[j]]=-mu[i];
            else break;
        }
    }
}

inline ll Mobius(int a,int b){

    ll res=0;
    for(int i=1;i<=a;i++) res+=(ll)mu[i]*(a/i)*(b/i);
    ll tmp=0;
    for(int i=1;i<=a;i++) tmp+=(ll)mu[i]*(a/i)*(a/i);
    return res-tmp/2;
}

int main(){

    int T,C=0;
    init();
    scanf("%d",&T);
    while(T--){

        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0){

          printf("Case %d: 0\n",++C);
          continue;
        }
        b/=k;
        d/=k;
        if(b>d) swap(b,d);
        printf("Case %d: %lld\n",++C,Mobius(b,d));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/81237245