莫比乌斯反演学习笔记

莫比乌斯反演学习笔记莫比乌斯反演的形式 另一种描述是 一种是和所有的约数有关一种是和所有的倍数有关 解题的时候要根据题目选择合适的表达形式 感觉第二种用的比较多 关于莫比乌斯函数 mu 他的定义如下 这个莫比乌斯函数有一些性质 1 2 一般需要预处理所有的莫比乌斯函数值 需要用到线性筛 mu 1 1 for inti 2

莫比乌斯反演的形式:

莫比乌斯反演学习笔记


另一种描述是:

莫比乌斯反演学习笔记


一种是和所有的约数有关一种是和所有的倍数有关,解题的时候要根据题目选择合适的表达形式,感觉第二种用的比较多。

关于莫比乌斯函数mu,他的定义如下:

莫比乌斯反演学习笔记


这个莫比乌斯函数有一些性质:

(1)

莫比乌斯反演学习笔记


(2)

莫比乌斯反演学习笔记



一般需要预处理所有的莫比乌斯函数值,需要用到线性筛

mu[1] = 1; for (int i = 2; i <= maxn; i++) { if (!vis[i]) { prime[cnt++] = i; mu[i] = -1; } for (int j = 0; j < cnt; j++) { if (i*prime[j] > maxn) break; vis[i*prime[j]] = 1; if (i%prime[j] == 0) { mu[i*prime[j]] = 0; break; } else { mu[i*prime[j]] = -mu[i]; } } } 

做了几道基础的莫比乌斯反演题目来加深一下

HDU1695:点击打开链接

题意是[1,n],[1,m]两个区间中有多少对数的gcd是k。

先把题目转化为[1,n/k],[1,m/k]两个区间中有多少对数互质,可以用容斥原理求出所有gcd不为1的对数求出结果,但是用莫比乌斯反演可以更加高效的求解。

假设f(x)表示gcd为x的对数,F(x)表示gcd为x倍数的对数,那么显然f(x)和F(x)满足莫比乌斯反演的第二种描述,我们需要求出f(1):

f(1)=mu(1)F(1)+mu(2)F(2)+mu(3)F(3)+….,显然对于区间[1,a],[1,b]有F(x)=(a/x)*(b/x),所以到x=min(a,b)之后就不需要累计了因为F(x)始终为0。

然后因为题目规定了(a,b)和(b,a)算成一种,所以最终结果需要处理一下。

#include 
   
     #include 
    
      #include 
     
       #include 
      
        using namespace std; #define maxn long long a, b, c, d, k; long long prime[maxn], cnt; bool vis[maxn]; int mu[maxn]; void get_mu () { cnt = 0; memset (vis, 0, sizeof vis); mu[1] = 1; for (long long i = 2; i <= ; i++) { if (!vis[i]) { prime[cnt++] = i; mu[i] = -1; } for (long long j = 0; j < cnt; j++) { if (i*prime[j] > ) break; vis[i*prime[j]] = 1; if (i%prime[j] == 0) { mu[i*prime[j]] = 0; break; } else { mu[i*prime[j]] = -mu[i]; } } } } long long solve (long long n, long long m) { long long ans = 0; for (long long i = 1; i <= n; i++) { ans += mu[i]*((long long)(n/i) * (long long)(m/i)); } return ans; } int main () { //freopen ("in.txt", "r", stdin); get_mu (); int t, kase = 0; scanf ("%d", &t); while (t--) { scanf ("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k); if (k == 0) { printf ("Case %d: 0\n", ++kase); continue; } b /= k; d /= k; if (b > d) swap (b, d); printf ("Case %d: %lld\n", ++kase, solve (b, d)-solve (b, b)/2); } return 0; } 
       
      
     
   

BZOJ2301:点击打开链接

和上面那题很类似。

假设经过处理上下界以后是求出[a,b],[c,d]的互质对数,如果ans(x,y)表示[1,x],[1,y]的互质对数,那么根据容斥原理,也就是求出ans(b,d)+ans(a-1,c-1)-ans(a-1,d)-ans(c-1,b)。

但是数据组数很多如果继续使用遍历复杂度将会达到O(q*n)显然会爆炸,可以使用分块加速思想。

也就是需要Σ[n/i]中间结果会有很多相同的数,可以发现[i,n/(n/i)]之间的结果是相等的,也就是F(i)到

F(min(n/(n/i), m/(m/i)))之间的结果是相同的,所以可以先预处理mu函数的前缀和。复杂度变成了O(q*sqrt (n))。

#include 
     
       #include 
      
        #include 
       
         #include 
        
          #include 
         
           using namespace std; #define maxn 51111 long long a, b, c, d, k; long long prime[maxn], cnt; bool vis[maxn]; int mu[maxn]; long long sum[maxn]; void get_mu () { memset (vis, 0, sizeof vis); cnt = 0; mu[1] = 1; for (int i = 2; i <= 50000; i++) { if (!vis[i]) { prime[cnt++] = i; mu[i] = -1; } for (int j = 0; j < cnt; j++) { if (i*prime[j] > 50000) break; vis[i*prime[j]] = 1; if (i%prime[j] == 0) { mu[i*prime[j]] = 0; break; } else { mu[i*prime[j]] = -mu[i]; } } } sum[0] = 0; for (int i = 1; i <= 50000; i++) sum[i] = sum[i-1]+mu[i]; } long long solve (long long a, long long b) { long long ans = 0; if (a > b) swap (a, b); long long j; for (long long i = 1; i <= a; i = j+1) { j = min (a/(a/i), b/(b/i)); ans += (sum[j]-sum[i-1])*(long long)(a/i)*(long long)(b/i); } return ans; } int main () { get_mu (); int t; scanf ("%d", &t); while (t--) { scanf ("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k); b /= k, d /= k; a = ceil (a*1.0/k), c = ceil (c*1.0/k); printf ("%lld\n", solve (b, d) + solve (a-1, c-1) - solve (a-1, d) - solve (c-1, b)); } return 0; } 
          
         
        
       
     

HDU4746:点击打开链接

题意:在区间[1,a][1,b]中有多少对数的gcd满足:他们的gcd分解后素因子数小于等于p。

同样的假设f(x)表示gcd为x的对数,F(x)表示gcd为x倍数的对数,可以有:

莫比乌斯反演学习笔记


于是就可以对于每一个i,求出这个i在自己的质因子数下对F(x)的贡献,然后通过前缀和和分块加速,和上题类似的做法就可以在O(q*sqrt(n))时间内求出结果。

#include 
      
        #include 
       
         #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; #define maxn vector 
             
               fac[maxn]; int prime[maxn], cnt; bool vis[maxn]; int mu[maxn]; int num[maxn]; //每个数能够分解成几个质数 long long sum[maxn][22]; long long n, m;int p; void init () { memset (vis, 0, sizeof vis); for (int i = 1; i <= ; i++) fac[i].clear (); cnt = 0; mu[1] = 1; for (int i = 2; i <= ; i++) { if (!vis[i]) { prime[cnt++] = i; mu[i] = -1; } for (int j = 0; j < cnt; j++) { if (i*prime[j] > ) break; vis[i*prime[j]] = 1; if (i%prime[j] == 0) { mu[i*prime[j]] = 0; break; } else { mu[i*prime[j]] = -mu[i]; } } } memset (vis, 0, sizeof vis); memset (num, 0, sizeof num); num[1] = 0; for (int i = 2; i <= ; i++) { if (!vis[i]) { for (int j = i; j <= ; j += i) { vis[j] = 1; int cur = j; while (cur%i == 0) { cur /= i; num[j]++; } } } } memset (sum, 0, sizeof sum); for (int i = 1; i <= ; i++) { for (int j = i; j <= ; j += i) { sum[j][num[i]] += mu[j/i]; } } for (int i = 1; i <= ; i++) { for (int j = 0; j <= 18; j++) { sum[i][j] += sum[i-1][j]; } } for (int i = 1; i <= ; i++) { for (int j = 1; j <= 18; j++) { sum[i][j] += sum[i][j-1]; } } } int main () { init (); int q; scanf ("%d", &q); while (q--) { scanf ("%lld%lld%d", &n, &m, &p); if (p >= 19) { printf ("%lld\n", n*m); continue; } p = min (18, p); long long ans = 0; int j; for (int i = 1; i <= min (n, m); i = j+1) { j = min (n/(n/i), m/(m/i)); ans += (long long)(sum[j][p]-sum[i-1][p])*(long long)(n/i)*(long long)(m/i); } printf ("%lld\n", ans); } return 0; } 
              
             
            
           
          
         
        
      













版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228824.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午6:07
下一篇 2026年3月16日 下午6:07


相关推荐

  • 墙裂推荐!Pycharm里6大神器插件!

    墙裂推荐!Pycharm里6大神器插件!大家好 我是菜鸟哥 又是一个周末到啦 如果说 Python 里面的最佳的开发工具 估计一只手都数不过来 比如有小家碧玉 sublimetext 小巧玲珑 atom 重型宝刀 pycharm 全

    2026年3月27日
    3
  • linux quotacheck命令参数及用法详解—Linux系统管理

    linux quotacheck命令参数及用法详解—Linux系统管理功能说明 检查磁盘的使用空间与限制 语 法 quotacheck nbsp adgRuv 文件系统 补充说明 执行 quotacheck 指令 扫描挂入系统的分区 并在各分区的文件系统根目录下产生 quota user 和 quota group 文件 设置用户和群组的磁盘空间限制 参 数 nbsp nbsp a nbsp nbsp nbsp 扫描在 etc fstab 文件里 有加入 quota 设置的分区 nbsp nbsp

    2026年3月19日
    1
  • 面试官:Java的重写和重载有什么区别?[通俗易懂]

    面试官:Java的重写和重载有什么区别?[通俗易懂]老读者都知道了,七年前,我从美女很多的苏州回到美女更多的洛阳(美化了),抱着一幅“从二线城市退居三线城市”的心态,投了不少简历,也“约谈”了不少面试官,但仅有两三个令我感到满意。其中有一位叫老马,至今还活在我的微信通讯录里。他当时扔了一个面试题把我砸懵了:“王二,Java的重写(Override)和重载(Overload)有什么区别?”那年我二十三岁,正值青春年华,大约就是周杰伦发布《八度空间…

    2025年10月16日
    5
  • netsh命令

    netsh命令netsh NetworkShell 是一个 windows 系统本身提供的功能强大的网络配置命令行工具 导出配置脚本 netsh cinterfaceip gt c interface txt 导入配置脚本 netsh fc interface txt Netsh 是命令行脚本实用工具 它允许从本地或远程显示或修改当前正在运行的计算机的网络配置 Netsh 还提供了一个脚本功能 对于指定计算机 可以通过此功能以批处理模式运行一组命令 为了存档或配置其他服务器 Nets

    2026年3月26日
    2
  • 数字信号处理matlab实验心得,数字信号处理学习心得体会3篇

    《数字信号处理》是我们通信工程和电子类专业的一门重要的专业基础课程,主要任务是研究数字信号处理理论的基本概念和基本分析方法,通过建立数学模型和适当的数学分析处理,来展示这些理论和方法的实际应用。数字信号处理技术正飞速发展,它不但自成一门学科,更是以不同形式影响和渗透到其他学科。以下是小编为大家精心准备的:,欢迎参考阅读!数字信号处理学习心得体会一随机数字信号处理是由多种学科知识交叉渗透形成的,在通…

    2022年4月17日
    170
  • mac上idea调整字体大小

    mac上idea调整字体大小点击 intelliIDEA gt Preferences gt Editor gt Colors amp Fonts gt Font 将默认 Size 的 12 改为 14 吧

    2026年3月4日
    7

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号