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

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

声明:本文思路来自大佬

HDU 1695
题意:
在这里插入图片描述
这就需要用到莫比乌斯反演

什么是莫比乌斯反演?

个人理解:
简单来说,就是给出一个函数 F(n),然后再由F(n)定义一个函数G(n);然后已知G(n)求 F(n),就可以通过反演由G(n)反向得到F(n)。
就是根据已知的给反推回去。

我们先来看一个函数
在这里插入图片描述
这里 d∣n 的意思是d能整除n,也就是说第一个函数可以由他的每一个因子带入另一个函数的和而求得,那我们先写出这个函数的前几项看看
在这里插入图片描述
然后我们试试用F(i)来表示f(i)那么就可以用几个F(i)的加减来解决了
在这里插入图片描述
在反推的过程中我们其实可以得到一些规律,f(n)所对应的 F(d) 必为其的一些系数,且F(d)前面的正负号与另一个函数有关,那么我们可以假写出一个式子
在这里插入图片描述
可以认为f(n)为F(d)乘上一个系数而得到的,且系数可以认为是一个函数μ(d),我们写出μ(d)的前几项来看看
在这里插入图片描述

i:		1		2		3		4		5		6		7		8		9		10		11		12

μ(i):	1		-1		-1		0		-1		1		-1		0		0		1		-1		0

素因数	1		2		3		2*2		5		2*3		7		2*2*2	3*3		2*5		11		2*2*3

这个函数其实就是莫比乌斯函数,他有如下的定义
其中μ(d)为莫比乌斯函数,其定义如下
在这里插入图片描述
这样的转换就叫做莫比乌斯反演,那么如何快速的求得莫比乌斯函数呢
莫比乌斯函数有以下几个性质
一、对于任意正整数n有
在这里插入图片描述
二、莫比乌斯函数是积性函数
设f(n)为一个定义在N+集合上的函数,如果对于任意(x, y)=1有f(xy)= f(x)f(y),则称f(n)为一个积性函数; 若对于任意x和y均有f(xy)= f(x)f(y),则称f(n)为一个完全积性函数
由于莫比乌斯函数是一个积性函数所以我们就可以用线性筛来求得莫比乌斯函数的值了

int vis[N];
int mu[N];
int prime[N];
int cnt;
void Init(){
    memset(vis,0,sizeof(vis));
    mu[1] = 1;
    cnt = 0;
    for(int i=2; i<N; i++) {
        if(!vis[i]) {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j=0; j<cnt&&i*prime[j]<N; j++) {
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mu[i*prime[j]] = -mu[i];
            else {
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}

在这里插入图片描述
那我们可以用莫比乌斯反演来解决一下这个问题
在这里插入图片描述

	long long a,b,c,d,k;
    scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
    if(k==0)
    {
        printf("Case %d: 0\n",cas++);
        continue;
    }
    b=b/k;
    d=d/k;
    long long ans1=0,ans2=0;
    for(int i=1; i<=min(b,d); i++)
    	ans1+=mu[i]*(b/i)*(d/i);

此时的 ans1 还不是我们要的答案,此时的比如 1<=x<=3,1<=y<=4;
(x,y) => (1,1) (1,2) (1,3) (1,4) (2,1) (2,2) (2,3) (2,4) (3,1) (3,2) (3,3) (3,4)
可以发现(1,2)(2,1)(1,3)(3,1)(2,3)是重复的
这个时候就需要容斥一下

for(int i=1; i<=min(b,d); i++)//容斥的部分
            ans2+=mu[i]*(min(b,d)/i)*(min(b,d)/i);
        ans1-=ans2/2;

最后附上代码:

#include <bits/stdc++.h>
#define MAX_INT  ((unsigned)(-1)>>1)
#define MIN_INT  (~MAX_INT)
#define db printf("where!\n");
using namespace std;
#define ll long long
template<class T>inline void read(T &res){
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
const int N=100005;
int vis[N];
int mu[N];
int prime[N];
int cnt;
void Init(){
    memset(vis,0,sizeof(vis));
    mu[1] = 1;
    cnt = 0;
    for(int i=2; i<N; i++) {
        if(!vis[i]) {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j=0; j<cnt&&i*prime[j]<N; j++) {
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mu[i*prime[j]] = -mu[i];
            else {
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}
int main()
{
    Init();
    int cas=1;
    int t;cin>>t;
    while(t--){
        long long a,b,c,d,k;
        scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",cas++);
            continue;
        }
        b=b/k;
        d=d/k;
        long long ans1=0,ans2=0;
        for(int i=1; i<=min(b,d); i++)
            ans1+=mu[i]*(b/i)*(d/i);
        for(int i=1; i<=min(b,d); i++)//容斥的部分
            ans2+=mu[i]*(min(b,d)/i)*(min(b,d)/i);
        ans1-=ans2/2;
        printf("Case %d: %lld\n",cas++,ans1);
    }
    return 0;
}

关于反演入门,还有一篇文章个人觉得写得不错

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值