HDU 1695 GCD (莫比乌斯反演入门学习小结)

来源:互联网 发布:华大基因 云计算 编辑:程序博客网 时间:2024/06/06 03:04

前言:

这些天在学习莫比乌斯的过程中看了许多博客和众多大牛的解释,然而可是博主太菜的原因,一直没能好好理解,今天偶然间看到了一份吉大附中的ppt,感觉瞬间开了窍,所以先把这个材料推荐给大家,然后我再这里总结一下自己的体会。

ppt地址: https://wenku.baidu.com/view/e6645609d15abe23492f4db0.html
(以下图片取自ppt,仅供学习与交流使用)

这里写图片描述

上图中的miu函数我们暂时不去在意,这个函数是莫比乌斯发现的一个刚好满足我们需要的性质的函数,暂时不考虑太多。(之后我们线性筛o(n)的筛出miu函数的值)
上边的公式是形式一,我们经常用他的另一种形式:

这里写图片描述

第二张图中左边的式子称为原式,右边的式子称为反演式。
然后我们先来解释一下式子中的符号吧:连加符号我就不多说了,重点说一下 n|d 这个符号吧,意思是d是n的倍数(换句话说,n是d的因子,n整除d)。连加符号代表枚举所有的d。

为什么要用莫比乌斯反演?

因为 f(n) 在很多时候很难求出来,而 F(n) 却可以暴力莽出来。举个栗子(大概浏览一下,不要仔细看):

2

这里写图片描述

然而我们这道题(hdu 1695)却无需那么麻烦,两个区间都是从1开始的,那么我们按照这个思路反演一下,就能得到所有有序对的组合了。
(然而的然而,本题要求(x,y)和(y,x)是一个。。
所以我们考虑我们已经得到的区间 (1,b)(1,d) (b<=d) 的有序对的个数,那么我们可知重复的一定是在 (1,b)(1,b) 中,所以减去其中的一半就是答案。也就是说做两次莫比乌斯反演。

下边是线性筛 素数(prime)+欧拉数(phi)+μ值(miu)基础的板子

#include<stdio.h>#include <iostream>#include<string.h>#include<math.h>#include<algorithm>#define eps 1e-8typedef long long int lli;using namespace std;const int maxn = 1e6+10;bool isprime[maxn] = {1,1};//int phi[maxn];int prime[maxn],miu[maxn];void moblus(){    int cnt = 0;miu[1] = 1;    for(lli i = 2;i < maxn;i++){        if(!isprime[i]){            prime[cnt++] = i,miu[i] = -1;//phi[i] = i-1;        }        for(lli j = 0;j < cnt && i*prime[j] < maxn;j++){            lli x = prime[j];            isprime[i*x] = 1;            if(i%x){                miu[i*x] = -miu[i];                //phi[i*x] = phi[i] * phi[x];            }            else{                miu[i*x] = 0;                //phi[i*x] = phi[i] * x;                break;            }        }    }}int main(){    moblus();    lli n,p,cas,ncase = 0;    scanf("%d",&cas);    moblus();    while(cas--){        ncase++;        lli a,b,c,d,k;        scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);        if(k == 0){            printf("Case %lld: 0\n",ncase);            continue;        }        if(d<b) swap(d,b);        lli ans = 0,ans2 = 0;        for(int i = 1;;i++){            if(i*k > b) break;            ans += miu[i] * (b/(i*k)) * ((d/(i*k)));        }        for(int i = 1;;i++){            if(i*k > b) break;            ans2 += miu[i] * (b/(i*k)) * ((b/(i*k)));        }        printf("Case %lld: %lld\n",ncase,ans-ans2/2);    }    return 0;}
原创粉丝点击