zoukankan      html  css  js  c++  java
  • 莫比乌斯反演入门

    莫比乌斯反演

    莫比乌斯反演是数论中的一个重要内容,可以化简很多数论函数的计算。

    本文形式化过程偏多,一定要耐心看完并试着自己推导。

    前置芝士

    >莫比乌斯函数<

    定义

    对于定义在自然数域的两个函数 (F(x))(f(x)) ,若两函数满足

    [F(n)=sum_{d|n}f(d) ]

    则有

    [f(n)=sum_{d|n}mu(d)F(frac{n}{d}) ]

    莫比乌斯反演还有另外一种常用的形式:

    [F(n)=sum_{n|d}f(d)\ Rightarrow f(n)=sum_{n|d}mu(frac{d}{n})F(d) ]

    证明:

    [egin{aligned} sum_{d|n}mu(d)F(frac{n}{d}) & =sum_{d|n}mu(d)sum_{d^{prime}|frac{n}{d}}f(d^{prime})\ & =sum_{d^{prime}|n}mu(d)sum_{d|frac{n}{d}}f(d)\ & =f(n) end{aligned} ]


    使用例:

    [sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)=1] ]

    不妨假设这里的(n<m)

    对于gcd这个东西很套路,先设

    [f(x)=sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)=x]\ g(x)=sum_{x|d}f(d) ]

    其中 (dleq n)

    代入莫比乌斯反演公式2,得到:

    [f(x)=sum_{x|d}mu(frac{d}{x})g(d)\ Rightarrow f(1)=sum_{1|d}mu(d)g(d)\ ]

    修改枚举项,可以得到

    [f(1)=sum_{i=1}^{n}mu(i)g(i) ]

    (mu(i)) 再好求不过了,关键在于 (g(i)) 是个什么东西。

    从含义出发,易得到:

    [g(x)=sum_{i=1}^{n}sum_{j=1}^{m}[x|gcd(i,j)]\ g(x)=sum_{i=1}^{n/x}sum_{j=1}^{m/x}[1|gcd(i,j)]\ ]

    (1|gcd(i,j)) 显然恒成立,则

    [g(x)=frac{n}{x}cdot frac{m}{x} ]

    (O(1)) 算就可以了。

    最后我们求的是

    [Ans=f(1)=sum_{i=1}^{n}mu(i)lfloorfrac{n}{i} floorlfloorfrac{m}{i} floor ]

    所以只需要 (O(n))(mu(i)) 前缀和即可。

    最后代码和>莫比乌斯函数<里的没有太大区别


    例题

    P2398 GCD SUM

    >题目链接<

    题目描述

    [sum_{i=1}^{n}sum_{j=1}^{n}gcd(i,j) ]

    输入格式

    一行一个正整数 (n)

    输出格式

    一行一个整数表示答案

    数据范围

    (nleq 10^5)


    解析

    这里直接讲 (sum_{i=1}^{n}sum_{j=1}^{m}gcd(i,j)) 的做法

    直接拿给我们是没有办法做的。

    不妨设(n < m),实际代码中 (n=min(n,m)) 即可。这类有限项枚举求和交换枚举项顺序一般是没有问题的。

    考虑枚举gcd,能化得如下式子

    [sum_{d=1}^{n}sum_{i=1}^{n}sum_{j=1}^{m}dcdot[gcd(i,j)=d]\ =sum_{d=1}^{n}dsum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)=d]\ =sum_{d=1}^{n}dsum_{i=1}^{n/d}sum_{j=1}^{m/d}[gcd(i,j)=1] ]

    后面这一团不就是之前推的式子?

    直接套结论

    [sum_{d=1}^{n}dsum_{i=1}^{n/d}sum_{j=1}^{m/d}[gcd(i,j)=1]\ =sum_{d=1}^{n}dsum_{i=1}^{n/d}mu(i)lfloorfrac{n}{id} floorlfloorfrac{m}{id} floor ]

    可以知道,对于特定区间内的 (d)(n/d) 是无变化的。

    所以可以对前面的 (d) 进行数论分块。

    后面的求值参考上面,也可以数论分块。

    总时间复杂度 (O(sqrt{n} imessqrt{n})=O(n))

    落到本题上,本题的 (n=m) , 数论分块的取 (min(n/(n/i),m/(m/i))) 操作都免了。

    参考code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1e6;
    
    int primes[N],tot=0;
    bool mp[N];
    
    int mu[N],sum_[N];
    
    void init(int n)
    {
    	mu[1]=1;
    	for(int i=2; i<=n; i++)
    	{
    		if(!mp[i])
    		{
    			primes[++tot]=i;
    			mu[i]=-1;
    		}
    		for(int j=1; primes[j]*i<=n; j++)
    		{
    			int tmp=primes[j]*i;
    			mp[tmp]=1;
    			if(i%primes[j]==0)
    			{
    				mu[tmp]=0;
    				break;
    			}
    			mu[tmp]=mu[i]*-1;
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		sum_[i]=sum_[i-1]+mu[i];
    	}
    }
    
    ll solve(ll a,ll b)
    {
    	ll res=0;
    	for(int i=1,j; i<=a; i=j+1)
    	{
    		j=min(a/(a/i),b/(b/i));
    		res+=(ll)(sum_[j]-sum_[i-1])*(a/i)*(b/i);
    	}
    	return res;
    }
    
    int main()
    {
    	init(1e5+10);
    
    	int n;
    	scanf("%d",&n);
    	ll ans=0;
    	for(int i=1,j; i<=n; i=j+1) //第一遍整除分块前面的d
    	{
    		j=n/(n/i);
    		ll tmp=(ll)(i+j)*(j-i+1)/2;
    		ans+=(ll)tmp*solve(n/i,n/i);//第二遍整除分块
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    P2568 GCD

    >题目链接<

    题目描述

    给定正整数 (n),求 (1le x,yle n)(gcd(x,y)) 为素数的数对 ((x,y)) 有多少对。

    输入格式

    只有一行一个整数,代表 (n)

    输出格式

    一行一个整数表示答案。

    数据范围

    (1le n le 10^7)


    解析

    题目求

    [sum_{i=1}^{n}sum_{j=1}^{n}[gcd(i,j) ext{是素数}] ]

    (gcd(i,j)=d),则

    [ ext{原式}=sum_{d=1}^{n}d[d ext{是素数}]sum_{i=1}^{n/d}sum_{j=1}^{n/d}[gcd(i,j)=1] ]

    后面那个东西怎么做不用重复说了吧。

    至于前面,对 (d) 再进行一次数论分块 (有向下取整除法的地方就可以考虑一下数论分块)

    再处理一个素数个数前缀和就行了。

    code:(仅供思路参考,下面这份代码以 2.82s/247.67MB 的好成绩惊险卡过)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1e7+10;
    
    ll primes[N],psum[N],tot=0;
    bool mp[N];
    
    ll mu[N],sum[N];
    
    void init(ll n)
    {
    	mu[1]=1,mp[1]=1;
    	for(int i=2; i<=n; i++)
    	{
    		if(!mp[i])
    		{
    			primes[++tot]=i;
    			mu[i]=-1;
    		}
    		for(int j=1; (ll)primes[j]*i<=n; j++)
    		{
    			ll tmp=(ll)primes[j]*i;
    			mp[tmp]=1;
    			if(i%primes[j]==0)
    			{
    				mu[tmp]=0;
    				break;
    			}
    			mu[tmp]=mu[i]*-1;
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		sum[i]=sum[i-1]+mu[i];
    		if(mp[i]) psum[i]=psum[i-1];
    		else psum[i]=psum[i-1]+1;
    	}
    }
    
    ll solve(ll a,ll b)
    {
    	ll res=0;
    	for(int i=1,j; i<=a; i=j+1)
    	{
    		j=min(a/(a/i),b/(b/i));
    		res+=(ll)(sum[j]-sum[i-1])*(a/i)*(b/i);
    	}
    	return res;
    }
    
    int main()
    {
    	init(1e7+10);
    
    	int n;
    	scanf("%d",&n);
    	ll ans=0;
    	for(int i=1,j; i<=n; i=j+1)
    	{
    		j=n/(n/i);
    		ans+=(ll)(psum[j]-psum[i-1])*solve(n/i,n/i);
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    P2257 YY的GCD

    >题目链接<

    题目描述

    神犇 YY 虐完数论后给傻× kAc 出了一题

    给定 (N, M),求 (1 leq x leq N)(1 leq y leq M)(gcd(x, y)) 为质数的 ((x, y)) 有多少对。

    输入格式

    第一行一个整数 (T) 表述数据组数。

    接下来 (T) 行,每行两个正整数,(N, M)

    输出格式

    (T) 行,每行一个整数表示第 (i) 组数据的结果。

    数据范围

    (T=10^4 , N,Mle 10^7)


    解析

    乍看跟上面那个题几乎完全相同

    但是是多组询问,按照上面那个级别的优秀时空复杂度是肯定过不了的。

    再往下推一下式子(这里已经把 (n,m) 换上去了,(n,m) 相当于题中的 (N,M) ,且 (nle m) )

    (T=id)

    [egin{aligned} Ans & =sum_{d=1}^{n}d[d ext{是素数}]sum_{i=1}^{n/d}mu(i)lfloorfrac{n}{id} floorlfloorfrac{m}{id} floor\ & =sum_{T=1}^{n}lfloorfrac{n}{T} floorlfloorfrac{m}{T} floorsum_{d|T}[d ext{是素数}]mu(frac{T}{d}) end{aligned} ]

    按照惯例,我们要想一下后面那团东西的前缀和怎么求。

    可以从线性筛出发考虑。

    令函数 (lambda(x)=sum_{d|x}[d ext{是素数}]mu(frac{x}{d})) ,求这个函数的前缀和。

    首先,(lambda(1)=0,lambda( ext{质数})=1)

    (x=icdot y)(y)(x) 的最小质因子。

    1.(y|i) 时,即 (x) 有多个最小质因子:

    • (i) 质因子互不相同时,当且仅当枚举的素数 (d=y)(mu(frac{x}{d}) e 0)

      此时:(lambda(x)=mu(frac{x}{y}))

    • (i) 有相同质因子,则对于任意枚举的素数 (d)(frac{x}{d}) 内都有相同质因子, 即 (mu(frac{x}{d})=0)

      此时仍有 (lambda(x)=mu(frac{x}{y}))

    2.(y mid i) 时,即 (x) 只有一个最小质因子:

    [lambda(x)=sum_{d|x}[d ext{是素数}]mu(frac{x}{d})\ Rightarrow lambda(icdot y)=sum_{d|(icdot y)}[d ext{是素数}]mu(frac{icdot y}{d}) ]

    我们已经知道,(mu(icdot y)=-mu(i)) (如果看不懂可以再看看莫比乌斯函数定义)

    所以对于 (lambda(i)) 其中的每一项的相反数都被包括在了 (lambda(x)) 中,且 (lambda(x)) 只是多了一个 (mu(frac{icdot y}{y})=mu(i))

    所以此时的 (lambda(x)=-lambda(i)+mu(i))

    综上:

    [lambda(x)= egin{cases} mu(1) , & x ext{是质数}\ mu(i) , & y|i\ -lambda(i)+mu(i) , & y mid i end{cases} ]

    于是 (lambda(x)) 就可以用线筛求了。

    code: 7.79s/90.86MB

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1e7+10;
    
    int primes[N],tot=0;
    int mu[N],lam[N];
    bool mp[N];
    
    void init(int n)
    {
    	mu[1]=mp[1]=1;
    	for(int i=2; i<=n; i++)
    	{
    		if(!mp[i]) primes[++tot]=i,mu[i]=-1,lam[i]=1;
    		for(int j=1; primes[j]*i<=n; j++)
    		{
    			int x=i*primes[j];
    			mp[x]=1;
    			if(i%primes[j]==0)
    			{
    				lam[x]=mu[i];
    				mu[i]=0;
    				break;
    			}
    			lam[x]=-lam[i]+mu[i];
    			mu[x]=-1*mu[i];
    		}
    	}
    	for(int i=1; i<=n; i++)
    		lam[i]+=lam[i-1];//前缀和
    }
    
    int main()
    {
    	init(1e7);
    	int t;
    	scanf("%d",&t);
    	while(t--)
    	{
    		int n,m;
    		scanf("%d%d",&n,&m);
    		ll ans=0;
    		if(n>m) swap(n,m);
    		for(int i=1,j; i<=n; i=j+1)
    		{
    			j=min(n/(n/i),m/(m/i));
    			ans+=(ll)(lam[j]-lam[i-1])*(n/i)*(m/i);
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    P1829 [国家集训队]Crash的数字表格/JZPTAB

    P6156 简单题

    P3768 简单的数学题

    P3704 [SDOI2017]数字表格

    P5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题

    (在做了在做了)

    参考文章

    我也不知道什么是"莫比乌斯反演"和"杜教筛"

  • 相关阅读:
    SpringCloud------链路追踪组件Sleuth
    SpringCloud------Zuul过滤器结合谷歌Gauva现实限流
    SpringCloud------Zuul网关
    极大团(maximal clique)算法:Born_kerbosch算法
    IDEA Cannot Resolve Symbol 问题的解决方法汇总
    idea 编译项目内存溢出OutMemoryError
    java 泛型和object比较
    java log4j 打日志到控制台同时打印到不同文件
    Java通过继承外部类来建立该外部类的protected内部类的实例(转)
    C#中的委托和事件
  • 原文地址:https://www.cnblogs.com/IzayoiMiku/p/14132541.html
Copyright © 2011-2022 走看看