您的位置:首页 > 其它

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

2017-08-07 15:46 447 查看

前言:

这些天在学习莫比乌斯的过程中看了许多博客和众多大牛的解释,然而可是博主太菜的原因,一直没能好好理解,今天偶然间看到了一份吉大附中的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) 却可以暴力莽出来。举个栗子(大概浏览一下,不要仔细看):





然而我们这道题(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-8
typedef 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: