• 『笔记』莫比乌斯反演


    写在前面

    该来的总会来,终究逃不过圈套。。

    爱数学!但是对这种个人认为极其复杂的数学有种莫名的恐惧。。

    前置芝士

    反演

    什么是反演?

    参照学长 LB 的 「笔记」反演

    积性函数

    定义域为正整数的算术函数 (f(x)) 满足

    [left{ egin{array}{l} f(n)=1 & n=1 \ f(pq)=f(p) cdot f(q) & p perp q end{array} ight. ]

    一些常见的积性函数

    • 欧拉函数 (varphi(n) = sum limits_{i=1}^n [gcd(i,n)=1])

    • 莫比乌斯函数 (mu(n))

    • 最大公约数 (gcd(n,k))

    完全积性函数

    定义域为正整数的算术函数 (f(x)) 满足

    [left{ egin{array}{l} f(n)=1 & n=1 \ f(ad)=f(a) cdot f(b) & a,b ext{任意} end{array} ight. ]

    常见完全积性函数

    • 恒等函数 (I(n)=1)

    • 元函数 (epsilon(n)=[n=1])

    • 单位函数 (id(n)=n)

    狄利克雷卷积(Dirichiet)

    对于两算术函数 (f,g) ,定义 ((f * g)(n )=sum_{d mid n} f(d) gleft(frac{n}{d} ight)) ,其中 (n) 常常省略不写。

    运算律

    交换律

    [f*g=g*f ]

    结合律

    [(f*g)*h=f*(g*h) ]

    分配律

    [f*(g+h)=f*g+f*h ]

    应用

    理论 1

    [mu * I =epsilon ]

    其中

    [egin{aligned} &I(n)=1,\ &epsilon(n)= left{ egin{array}{l} 1 & n=1 \ 0 & n eq 1 end{array} ight. end{aligned} ]

    则有

    [egin{aligned} epsilon(n) &= sum limits_{d mid n} mu(d) cdot I(cfrac{n}{d})\ &=sum limits_{d mid n} mu(d) end{aligned} ]

    理论 2

    [varphi * I =id ]

    其中 (id(n)=n)

    则有

    [egin{aligned} id(n) &= sum limits_{d mid n} varphi(d) cdot I(cfrac{n}{d}) \ &= sum limits_{d mid n} varphi(d) \ &= n end{aligned} ]

    任何数的 (varphi) 之和都是它本身

    理论 3

    [mu * id =varphi ]

    证明:

    [egin{aligned} mu * id &= mu * I * varphi\ &= epsilon * varphi end{aligned} ]

    其中

    [egin{aligned} (epsilon* varphi) (n) &= sum limits_{d mid n} epsilon(d) cdot varphi(cfrac{n}{d})\ &= varphi(n) end{aligned} ]

    所以 (mu * id = varphi)

    注意:(epsilon) 可看作数论函数中的 (1)

    莫比乌斯函数

    这里详细说明一下上文提到的 (mu) ,即莫比乌斯函数

    定义

    莫比乌斯函数定义为

    [mu(n)= left{ egin{array}{ll} 1 & n=1 \ 0 & n ext { 含有平方因子 } \ (-1)^{k} & k ext { 为 } n ext { 的本质不同质因子个数 } end{array} ight. ]

    性质

    莫比乌斯函数是积性函数,具有一下性质

    [sum_{d mid n} mu(d)=[n=1] ]

    证明

    (n=prod limits_{i=1}^{k} p_{i}^{c_{i}}, n^{prime}=prod limits_{i=1}^{k} p_{i})

    莫比乌斯函数定义

    [sum limits_{d mid n} mu(d)=sum limits_{d mid n^{prime}} mu(d) ]

    (n^prime) 的某因子 (d) ,有 (mu(d)=(-1)^prime) ,则它由 (i) 个本质不同的质因子组成。

    由于质因子总数为 (k),满足上式的因子数为 (C_k^i)

    对于原式,可转化为枚举 (mu(d)) 的值

    [sum limits_{d mid n^{prime}} mu(d)=sum limits_{i=0}^{k} C_{k}^{i} imes(-1)^{i}=sum_{i=0}^{k} C_{k}^{i} imes(-1)^{i} imes 1^{k-i} ]

    根据二项式定理,上式可化为

    [[1+(-1)]^k ]

    显然当 (k=0) ,即 (n=0) 时为 (1) ,否则为 (0)

    线性筛求莫比乌斯函数

    (mu) 为积性函数,因此可以线性筛求莫比乌斯函数。

    /*
    线性筛求莫比乌斯函数
    
    By Luckyblock
    
    有删改。
    */
    int pnum, mu[_], p[_];
    bool vis[_];
    
    void Euler(int lim_)
    {
        vis[1] = true;
        mu[1] = 1ll;
        for (int i = 2; i <= lim_; ++i)
        {
            if (!vis[i])
            {
                p[++pnum] = i;
                mu[i] = -1;
            }
            for (int j = 1; j <= pnum && i * p[j] <= lim_; ++j)
            {
                vis[i * p[j]] = true;
                if (!(i % p[j]))
                { //勿忘平方因子
                    mu[i * p[j]] = 0;
                    break;
                }
                mu[i * p[j]] = -mu[i];
            }
        }
    }
    
    

    莫比乌斯反演

    定义

    (f(n),g(n)) 两个数论函数,

    [g(n)=sum limits_{dmid n} f(d) ]

    则有

    [egin{aligned} f(n) &= (mu * f)(n) \ &= sum limits_{d mid n}mu(d) f(cfrac{n}{d}) end{aligned} ]

    证明

    利用狄利克雷卷积卷积,原问题可化为

    已知 (f = g * I) ,证明 (g= f * mu)

    显然有

    [egin{aligned} f * mu &= g * I * mu \ &Longrightarrow f * mu \ &= g * e \ &= g end{aligned} ]

    证毕。

    应用

    这里有一个套路,一般涉及到莫比乌斯反演的题目都可以按照这个思路来。

    zhx 亲授/kel

    (sum limits_{i=1}^n sum limits_{j=1}^m gcd(i,j)) 的值。

    “反正这题数据范围暴力肯定过不了,其他无所谓。——zhx

    显然,直接暴力枚举 (O(nmlog))肯定过不了。。

    能否在 (O(n)) 的时间内求解呢?

    这里可以用一个非常奇妙的思想——求和变形

    1. 增加枚举变量

      (id = varphi * I)

      [egin{align} & sum limits_{i=1}^n sum limits_{j=1}^m gcd(i,j) \ =& sum limits_{i=1}^n sum limits_{j=1}^m id[ gcd(i,j)] \ =& sum limits_{i=1}^n sum limits_{j=1}^m sum limits_{d mid gcd(i,j)} varphi(d) * I \ =& sum limits_{i=1}^n sum limits_{j=1}^m sum limits_{d mid gcd(i,j)} varphi(d) end{align} ]

    2. 交换枚举顺序

      [egin{align} &sum limits_{i=1}^n sum limits_{j=1}^m sum limits_{d mid gcd(i,j)} varphi(d) ag{4}\ &=sum limits_{d=1}^n sum limits_{i=1}^{leftlfloorfrac{n}{d} ight floor} sum limits_{j=1}^{leftlfloorfrac{m}{d} ight floor} varphi(d) ag{5} end{align} ]

    3. 删除无用变量

      [egin{align} & sum limits_{d=1}^n sum limits_{i=1}^{leftlfloorfrac{n}{d} ight floor} sum limits_{j=1}^{leftlfloorfrac{m}{d} ight floor} varphi(d) ag{5} \ =& sum limits_{d=1}^n leftlfloorcfrac{n}{d} ight floor leftlfloorcfrac{m}{d} ight floor varphi(d) ag{6} end{align} ]

    然后就可以直接枚举 (d=1 sim n) 求解答案。

    时间复杂度 (O(n))

    P2398 GCD SUM

    /*
    
    Name: P2398 GCD SUM
    
    Solution: 
       
    
    By Frather_
    
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #define int long long
    #define InF 0x3f3f3f3f
    #define kMax 10e5
    #define kMin -10e5
    #define kMod 998244353
    using namespace std;
    /*==================================================快读*/
    inline int read()
    {
        int X = 0, F = 1;
        char CH = getchar();
        while (CH < '0' || CH > '9')
        {
            if (CH == '-')
                F = -1;
            CH = getchar();
        }
        while (CH >= '0' && CH <= '9')
        {
            X = (X << 3) + (X << 1) + (CH ^ 48);
            CH = getchar();
        }
        return X * F;
    }
    /*===============================================定义变量*/
    int n;
    
    const int _ = 1000010;
    
    int prime[_], v[_], phi[_], cnt;
    
    int res[_];
    
    int ans;
    /*=============================================自定义函数*/
    void eular(int n) //线性筛筛欧拉函数
    {
        memset(v, 0, sizeof(v));
        phi[1] = 1;
    
        for (int i = 2; i <= n; i++)
        {
            if (!v[i])
            {
                prime[++cnt] = v[i] = i;
                phi[i] = i - 1;
            }
            for (int j = 1; j <= cnt; j++)
            {
                if (prime[j] > v[i] || i * prime[j] > n)
                    break;
                v[i * prime[j]] = prime[j];
                phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
            }
            // sumPhi[i] = sumPhi[i - 1] + phi[i];
        }
    }
    /*=================================================主函数*/
    signed main()
    {
        n = read();
        eular(n);
        for (int i = 1; i <= n; i++)
            ans += (n / i) * (n / i) * phi[i];
        printf("%lld
    ", ans);
        return 0;
    }
    

    最后

    鸣谢 :

    如果存在错误,感谢指出。

  • 相关阅读:
    Java Character类
    JavaMath&Number类
    oracle中创建数据库实例
    Jenkins安装和配置
    crontab计划 -计划任务和日志管理
    centos修改默认字符编码为GBK(服务器端java GBK字符乱码)
    使用vsphere client 克隆虚拟机
    建索引让SQL飞起来
    oracle中对LONG列进行查询
    优化sql,返回行数少情况下,NL比hash快好多
  • 原文地址:https://www.cnblogs.com/Frather/p/14742662.html
Copyright © 2020-2023  润新知