You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

莫比乌斯函数

【数论】为什么莫比乌斯函数长这样?这样理解最自然!

【数论】莫比乌斯函数/莫比乌斯反演

https://blog.csdn.net/qq_49593247/article/details/120394226

https://www.cnblogs.com/letlifestop/p/10262757.html

// https://blog.csdn.net/qq_42887171/article/details/95237740

要学习莫比乌斯函数需要学习 到 积性函数,深度理解 欧拉筛

一、积性函数

1. 定义

积性函数:若gcd(a,b)=1,且满足f(a*b)=f(a)*f(b),则称f(x)积性函数

完全积性函数:对于任意正整数ab,都满足f(a*b)=f(a)*f(b),则称f(x)完全积性函数

2. 性质

  1. f(n),g(n)均为积性函数,则函数h(n)=f(n)*g(n)也是积性函数

  2. f(n)为积性函数,则函数c*f(n)(c是常数)也是积性函数,反之一样

  3. 任何积性函数都能应用线性筛,在O(n)时间内求出1\sim n项(莫比乌斯函数要用到),像素数,欧拉函数等都是 积性函数

二、莫比乌斯函数

1. 用途

我们举例一道经典的应用题,求1N中与a互质的数的个数:根据容斥原理,设S_i1N中和a有公因子i的数的个数,答案为NS_2-S_3-S_5-S_7...+S_{2,3}+S_{3,5}+S_{2,5}...,可以发现,其答案为\large \displaystyle \sum_{i=1}^{N}\mu(i)*S_i

:在使用容斥原理解决计数问题时,莫比乌斯函数的值就是每一项的系数,要么是1,要么是-1。这是因为莫比乌斯函数在容斥原理中的作用就是用来表示每个子集合的权重,从而在计算中起到排除重复计数的作用。

2. 定义

莫比乌斯函数是个分段函数,它的意思:

  • ① 当n=1时,莫比乌斯函数值为1
  • ② 当n为可以分解为许多素数并且这些质因子的次数都是1时,莫比乌斯值就是-1质数因子个数的幂次方
  • ③ 除了这些情况剩余的情况莫比乌斯函数值都是0

前五十个数的莫比乌斯值.

打印前20个数字的莫比乌斯函数值

函数值 1 -1 -1  0 -1  1 -1  0  0  1 -1  0 -1  1  1  0 -1  0 -1  0 
 : 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

3. 求单个数字的莫比乌斯函数值

//单个数的莫比乌斯函数
int getmob(LL x) {
    int sum = 0;
    for (LL i = 2; i <= x / i; i++) { //从2开始一直到 sqrt(x),枚举所有可能存的小因子
        int cnt = 0;
        if (x % i == 0) {                     //如果x可以整除i
            while (x % i == 0) cnt++, x /= i; //计数并且不断除掉这个i因子
            if (cnt > 1) return 0;            //如果某个因子存在两个及以上个则返回0
            sum++;                            //记录因子个数
        }
    }
    if (x != 1) sum++;         //如果还存在另一个大因子,那么因子个数+1
    return (sum & 1) ? -1 : 1; //奇数个因子,返回-1,否则返回1
}

4.枚举倍数求莫比乌斯函数(埃氏筛)

//枚举倍数求莫比乌斯函数
LL mu[N] , sum[N];
void mobius(LL x) {
    mu[1] = 1;
    for (LL i = 1; i <= x; i++)
        for (LL j = i + i; j <= x; j += i)
            mu[j] -= mu[i];
    // 维护u(x)前缀和
    for (LL i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}

5.枚举约数求莫比乌斯函数(欧拉筛)

视频讲解

LL mu[N] , sum[N];
int primes[N], cnt;
bool st[N];
void get_mobius2(LL n) {
    mu[1] = 1;
    for (LL i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
            mu[i] = -1;
        }
        for (LL j = 0; primes[j] <= n / i; j++) {
            LL t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0) {
                mu[t] = 0;
                break;
            }
            mu[t] = mu[i] * -1;
        }
    }
     // 维护u(x)前缀和
    for (LL i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
}

6、性质I

该图意为n的所有因子的莫比乌斯值的和只有在n=1时成立为1,其余通通为0;

对这个性质的证明:

首先我们可以把n分解为很多个质数相乘(此时质数的幂不一定为1): 令 \large n=p_1^ {a_1}* p_2^ {a_2}* ... *p_k^{a_k}

\displaystyle \sum_{d | n} \mu(d) 这是求n的所有约数的莫比乌斯函数值的和,相当于在p_1,p_2,p_3,...这些素数中选择若干个质因子相乘来组成所有约数。

根据莫比乌斯函数性质,包含有素数平方的约数不用计算,他对答案的贡献值为0,所以我们可以把n分解的质数的次数全部消除为1,只有当莫比乌斯值为-1或者1时才对结果有贡献.那么问题就单纯的变为在kn的质因子中选0k个值组成约数,再将这些约数的值相加:

注意,这里的符号并不是全为加法,而是隔一个加隔一个减,这是因为莫比乌斯函数是积性函数,当选的数是奇数个时为值为负,反之为正,又因二项式定理,将-11带入,可以得到结果为0.

7、性质II

对任意正整数n

(ps:这条涉及莫比乌斯反演,还没学,学了再看)

题单

完全平方数

题意就是筛去完全平方数及其的倍数,然后输出第k个的值.

我们要求这个,就想到把1到ki的所有完全平方数和他的倍数筛去,但是一看数据,1e9,线性筛必定t,那再去想办法进行计算,我们先把2的平方4的倍数计算出来,在1到ki中,有ki/4个4的倍数,我们再计算的16的倍数个数时候,会发现在计算4的倍数个数时候已经把16的倍数个数计算过了,这里就重复了,而假设已经计算了4和9的倍数个数,再去计算36的倍数个数就会发现计算了两次,那么就要减去36的倍数个数,这里就已经想到可以用容斥了.这里我们发现这里需要枚举质数的平方的次数,且奇数偶数符号不相同,就会想到莫比乌斯函数.它计算枚举的边界是i*i<=n;我们再用n减去计算的出来的从2开始的到ki的完全平方数的个数即为所求:

当求出来之后,我们就可以用二分来求此时的值:

#include<iostream>
#define ll long long 
using namespace std;
int vis[40560],mo[40560],p[4253],n;
void init()
{
    int tot=0,k;
    mo[1]=1;
    for(int i=2;i<=40559;i++)
    {
        if(vis[i]==0)
        {
            p[++tot]=i;
            mo[i]=-1;
        }
        for(int j=1;j<=tot&&(k=i*p[j])<=40559;j++)
        {
            vis[k]=1;
            if(i%p[j]!=0)mo[k]=-mo[i];
            else 
            {
                mo[k]=0;
                break;
            }
        }
    }
}
bool judge(int x)
{
	ll ans=0;int i;
    for(int i=1;i*i<=x;i++)
    {
        ans+=mo[i]*(x/(i*i));
    }
    if(ans>=n)return true;
    else return false;
}
int main()
{
    int t;
    ll l,r,mid;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        l=n,r=1644934082;
        while(l<r) 
        {
            // cout<<l<<" oo "<<r<<endl;
			mid=(l+r)>>1;
			if(judge(mid)) 
                r=mid;
			else l=mid+1;
		}
        printf("%lld\n",r);
    }
    return 0;
}

例题:数字染色 题目意思:

给一个数组,在其中选数,看可以最多选出多少个gcd(最大公约数)>1的集合.

要求gcd>1,那我们就把他们的约数全部求出来,记录在一个si数组里(下标为这个约数的值,里面储存它作为约数被数组中的数用了多少次).然后我们遍历这个si数组,把每个约数可能组成的约数次数算出来,例如假设2作为约数被数组中的数被总共用了2次,那么它所有可以组成的约数就是1,2,4,也就是2^2-1种组合.而当我们选择计算了2的所有可以组成的公约数,3所有的可以组成的公约数,那么当我们计算6时,就会发现6已经被2和3计算过了,很好,容斥定理,用莫比乌斯函数作为系数求(2^n-1)的和,但是还有一个问题,当选择的是奇数个质数来计算个数时,应该加上,而莫比乌斯函数值为-1,而选偶数个时应该减去,所以在求莫比乌斯值时我们可以取个反,最终我们要求的式子就是:

#include<iostream>
#define ll long long
using namespace std;
ll a[200005],vis[200005],p[200005],mo[200005];
ll si[200005];
const ll mod=1e9+7;
void init()
{
    ll tot=0,k;
    mo[1]=1;
    for(int i=2;i<=200005;i++)
    {
        if(vis[i]==0)
        {
            p[++tot]=i;
            mo[i]=1;
        }
        for(int j=1;j<=tot&&(k=i*p[j])<=200005;j++)
        {
            vis[k]=1;
            if(i%p[j]==0)
            {
                mo[k]=0;
                break;
            }
            else mo[k]=-mo[i];
        }
    }
}
ll qpow(ll pp,ll x)
{
    ll ans=1;
    while(x)
    {
        if(x&1)ans=ans%mod*pp%mod;
        pp=pp*pp%mod;
        x>>=1;
    }
    return ans;
}
int main()
{
    ll n,ans=0;
    init();
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        for(int j=1;j*j<=a[i];j++)
        {
            if(j*j==a[i])
                si[j]++;
            else if(a[i]%j==0)
            {
                si[j]++;
                si[a[i]/j]++;
            }
        }
    }
    for(int i=2;i<=200005;i++)
    {   
        ans+=(mo[i]*(qpow(2,si[i])-1)%mod)%mod;       
    }
    printf("%lld",(ans%mod+mod)%mod);
    return 0;
}

ps:最后结果防止取模出负数要加上mod再取模.

之后会继续学习莫比乌斯反演的,如果本蒟蒻有什么错误望大佬指正.