莫比乌斯反演练习bzoj2440;bzoj2301;bzoj2820 YY的GCD

预备知识:

1. 莫比乌斯函数

1.1 莫比乌斯函数

μ(n)=1(1)k0n=1n=p1p2pk1

1.2 莫比乌斯函数的性质

d|nμ(d)=(n==1)

证明略。。

1.3 实现方法

线性筛。
线性筛法求素数的过程,每求出一个素数 p mu[p]=1;
筛非素数的过程,要break的地方, mu[iprime]=0 ;(因为它有幂数大于1的质因子);否则 mu[iprime]=mu[i] ;

void getmu(){
    cnt=0;mu[1]=1;
    for(int i=2;i<N;i++){
        if(!p[i]){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<N;j++){
            p[prime[j]*i]=1;
            if(i%prime[j]==0){
                mu[prime[j]*i]=0;
                break;
            }
            mu[prime[j]*i]=-mu[i];
        }
    }
}

2. 莫比乌斯反演

2.1 莫比乌斯反演定理

F(n)=d|nf(d)f(n)=d|nμ(d)F(nd)
或者另一种形式为
F(n)=n|df(d)f(n)=n|dμ(dn)F(d)
第二个比较常用。

2.1 莫比乌斯反演定理的证明

https://www.zhihu.com/question/23764267/answer/26007647
popoqqq也给出了一个证明。大体思想和上类似。不过还是觉得狄利克雷卷积看着更清爽。

2.2 莫比乌斯反演定理的应用

popoqqq-莫比乌斯反演
约数和与倍数和。

3. 习题

(题解基本都抄自POPOQQQ,感谢。)

3.1 bzoj2440: [中山市选2011]完全平方数
3.1.1 题目描述

求第k个不含有平方因子的数。多组数据。 T50,k109

3.1.2 题目分析

首先二分答案 问题转化为求 [1,x] 之间有多少个无平方因子数。
根据容斥原理可知 对于sqrt(x)以内所有的质数 有
x以内的无平方因子数
=0个质数乘积的平方的倍数的数的数量(1的倍数)
-每个质数的平方的倍数的数的数量(9的倍数,25的倍数,…)
+每2个质数乘积的平方的倍数的数的数量(36的倍数,100的倍数,…)-…
容易发现每个乘积a前面的符号恰好是 μ(a) (例如 μ(3)=1 ,故9对答案的贡献为负; μ(6)=1 ,故36对答案的贡献为正)
x 以内i2的倍数有 xi2 个 故有 Q(x)=xi=1μ(i)xi2
这题和莫比乌斯反演没关系,算是莫比乌斯函数的一个应用吧。。。

#include<cstdio>
#include<cmath>
#define ll long long
#define N 50005
using namespace std;
bool p[N]={1,1};
int prime[N],mu[N],cnt;
int t,n;
void getmu(){
    cnt=0;mu[1]=1;
    for(int i=2;i<N;i++){
        if(!p[i]){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<N;j++){
            p[prime[j]*i]=1;
            if(i%prime[j]==0){
                mu[prime[j]*i]=0;
                break;
            }
            mu[prime[j]*i]=-mu[i];
        }
    }
}
bool judge(ll x){
    int l=sqrt(x),ret=0;
    for(int i=1;i<=l;i++)
        ret+=x/i/i*mu[i];
    return ret>=n;
}
int main(){
    getmu();
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        ll l=0,r=n+n+1;
        while(l<r){
            ll mid=(l+r)/2;
            if(judge(mid)) r=mid;
            else l=mid+1;
        }
        printf("%lld\n",l);
    }
    return 0;
}
3.2 bzoj2301: [HAOI2011]Problem b
3.2.1 题目描述

n次询问,每次询问有多少个数对 (x,y) 满足 axb,cydgcd(x,y)=k N50000,1ab50000,1cd50000

3.2.2 题目分析

首先利用容斥原理将一个询问拆分成四个,每次询问有多少个数对(x,y)满足1<=x<=n,1<=y<=m且gcd(x,y)=k
这个问题等价于询问有多少个数对(x,y)满足1<=x<= nk ,1<=y<= mk 且gcd(x,y)=1
由于之前的结论,我们可以令f(i)为1<=x<=n,1<=y<=m且gcd(x,y)=i的数对(x,y)的个数,F(i)为1<=x<=n,1<=y<=m且i|gcd(x,y)的数对(x,y)的个数。(倍数和。)
我们求 f(1)
那么显然有 F(i)=nimi
反演后得 f(i)=i|dμ(nd)F(d)=i|dμ(di)ndmd
枚举原题中k的每一个倍数,我们就可以O(n)时间处理每个询问了
但是O(n)还是不能胜任本题的数据范围
考虑进一步优化
观察式子,发现 ni 最多有 2n 个取值
那么 nimi 就至多有 2n+2m 个取值
枚举这 2n+2m 个取值,对莫比乌斯函数维护一个前缀和,就可以在 O(nn) 时间内出解
总时间复杂度
枚举除法的取值这种方法在莫比乌斯反演的应用当中非常常用,且代码并不难写

#include<cstdio>
#include<algorithm>
#define N 50005
#define ll long long
using namespace std;
bool p[N]={1,1};
int prime[N],mu[N],sum[N],cnt,t,a,b,c,d,k;
void getmu(){
    mu[1]=1;cnt=0;
    for(int i=2;i<N;i++){
        if(!p[i]){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&i*prime[j]<N;j++){
            p[i*prime[j]]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<N;i++) sum[i]=sum[i-1]+mu[i];
    //for(int i=1;i<100;i++) printf("%d\n",mu[i]);
}
ll getans(int n,int m){
    ll ret=0;
    if(n>m) swap(n,m);
    for(int i=1,last;i<=n;i=last+1){
        last=min(n/(n/i),m/(m/i));
        ret+=(ll)(n/i)*(ll)(m/i)*(ll)(sum[last]-sum[i-1]);
    }
    return ret;
}
int main(){
    scanf("%d",&t);
    getmu();
    for(int i=1;i<=t;i++){
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        printf("%lld\n",getans(b/k,d/k)-getans((a-1)/k,d/k)-getans(b/k,(c-1)/k)+getans((a-1)/k,(c-1)/k));
    }
    return 0;
}
3.3 bzoj2820 YY的GCD
3.3.1 题目描述

求有多少数对(x,y)(1<=x<=n,1<=y<=m)满足gcd(x,y)为质数
n,m<= 107 数据组数<=10000

3.3.2 题目分析

根据上一题,我们枚举每一个质数 那么答案就是 ans=min(n,m)isprime(p)min(n,m)d=1μ(d)npdmpd
直接做显然TLE 考虑优化
T=pd ,那么有
ans=min(n.m)T=1npdmpdp|Tμ(Tp)
和上题的式子很像。我们如果求出 p|Tμ(Tp) 的前缀和,也就可以 O(n) 的时间内求解了。
枚举每个素数,更新它的倍数。复杂度 O(n)

#include<cstdio>
#include<algorithm>
#define N 10000005
#define ll long long
using namespace std;
ll ans;
int prime[N],mu[N],cnt,a[N],T,n,m;
bool p[N]={1,1};
void getmu(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!p[i]){
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&prime[j]*i<N;j++){
            p[i*prime[j]]=1;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
}
void init(){
    for(int i=1;i<=cnt;i++)
        for(int j=1;j*prime[i]<N;j++)
            a[prime[i]*j]+=mu[j];
    for(int i=1;i<N;i++) a[i]=a[i-1]+a[i];
}
void getans(int n,int m){
    ans=0;
    if(n>m) swap(n,m);
    for(int i=1,last;i<=n;i=last+1){
        last=min(n/(n/i),m/(m/i));
        ans+=(ll)(n/i)*(m/i)*(a[last]-a[i-1]);
    }
}
int main(){
    getmu();
    init();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        getans(n,m);
        printf("%lld\n",ans);
    }
    return 0;
}

更后话:
TAT再也不想用LaTeX了。
这个逼装的。。。好累啊。
以及自己不会反演啊怎么破눈_눈。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了python应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值