莫比乌斯反演学习笔记

莫比乌斯反演

前置知识: 数论分块 and 狄利克雷卷积

  • 数论分块

    用处:用于快速计算一些含有除法向下取整的和式

    for example: \(\sum_{i=1}^n \left\lfloor\frac{n}{i}\right\rfloor\)

    就是将 \(\left\lfloor\frac{n}{i}\right\rfloor\) 相等的数同时计算

    直接说结论吧:
    对于一个常数 \(n\) 使得 \(\left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{j}\right\rfloor\) 并且 \(i\le j\le n\)\(j\) 的最大值为\(\left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)

    证明: 令 \(k=\left\lfloor\frac{n}{i}\right\rfloor\)\(\therefore k\le \frac{n}{i}\)

    \(\therefore \left\lfloor\frac{n}{k}\right\rfloor \ge \left\lfloor\frac{n}{\frac{n}{i}}\right\rfloor = \left\lfloor i\right\rfloor = i\)

    \(j=\max\)满足条件的所有\(i = i_{max} = \left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)

    \(By\quad Oi-Wiki\)

    模板题:余数求和

    题目大意:给定正整数\(n,k(n,k \le 1e9)\),求\(\sum_{i=1}^n{k\mod i}\)

    solution:

    考虑推式子,将其转为数论分块的形式

    \(\sum_{i=1}^n k\mod i\)

    \(=\sum_{i=1}^n k-\left\lfloor\frac{k}{i}\right\rfloor*i\)

    \(=n*k-\sum_{i=1}^n\left\lfloor\frac{k}{i}\right\rfloor*i\)

    后面考虑数论分块,假设左端点为\(l\),右端点为\(r\),则该段的值\(=(k/l)*\sum_{i=l}^ri\)

    \(done.\)

    \(code:\)

    ll n,k;read(n,k);
    ll res = n*k;
    for(int l = 1,r;l <= n;l = r+1){
        r = (k/l)?min(k/(k/l),n):n;
        res = res - k/l*(l+r)*(r-l+1)/2;
    }
    write(res);
    
  • 狄利克雷卷积
    对于两个数论函数\(f(x)\)\(g(x)\),则它们的狄利克雷卷积得到的结果\(h(x)\)定义为

    \[h(x)=\sum_{d|x}f(d)g(\frac{n}{d})=\sum_{ab=x}f(a)g(b) \]

    简记为 \(h=f*g\)

    简单性质:

    1.\(交换律:f*g=g*f\)

    2.\(结合律:(f*g)*h=f*(g*h)\)

    3.\(分配律:(f+g)*h=f*h+g*h\)

    两个结论:

    1.两个积性函数的狄利克雷卷积也是积性函数

    2.积性函数的逆元也是积性函数

    详细证明请见\(oi-wiki\)

进入正题

  • 莫比乌斯函数

    \(\mu\)为莫比乌斯函数,定义为\(\mu=\begin{cases} 1,& n=1\\ 0,& n\text{含有平方因子}\\ (-1)^k,& k为n的本质不同质因子个数 \end{cases}\)

    性质:
    1.积性函数

    2.\(\sum_{d|x}\mu(d)=\begin{cases} 1,& n=1\\ 0,& n\ne1 \end{cases}\)
    \(\quad\)
    \(\mu*1=\epsilon\)

    性质二证明:

    \(n=\prod_{i=1}^k p^{c_i}_i , n^{'}=\prod_{i=1}^k p_i\)
    则有\(\sum_{d|n}\mu(d) = \sum_{d|n^{'}} = \sum_{i=0}^k\mathrm{C}_k^i*(-1)^i = (1+(-1))^k\)仅当\(k=0\)时为\(1\)

    反演结论:\([\gcd(i,j)=1]=\sum_{d|\gcd(i,j)}\mu(d)\)

  • 线性筛筛莫比乌斯函数
    运用定义式即可

    code:

    vector<int> prime;
    int mu[N];bitset<N> pd;
    inline void mobius(int n){
        mu[1]=1;
        for(int i=2;i<=n;++i){
            if(!pd[i])mu[i]=-1,prime.emplace_back(i);
            for(int j:prime){
                if(i*j>n) break;
                pd[i*j]=true;
                if(i%j == 0) break;
                else mu[i*j]=-mu[i];
            }
        }
    }
    
  • 莫比乌斯反演

    1. 应用反演结论\([\gcd(i,j)==1] = \sum_{d|\gcd(i,j)}\mu(d)\)

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

    1. 形式一\(\qquad if\quad f(n)=\sum_{d|n}g(d),then\quad g(n)=\sum_{d|n}\mu(d)f(\frac{n}{d})\)

      证明:

      \(\begin{aligned} \sum_{d\mid n}\mu(d)f\left(\frac{n}{d}\right) &= \sum_{d\mid n}\mu(d)\sum_{k\mid \frac{n}{d}}g(k)\\ &= \sum_{k\mid n}g(k)\sum_{d\mid \frac{n}{k}}\mu(d)\\ &= \sum_{k\mid n}\left[\frac{n}{k} = 1\right]g(k)\\ &= g(n)\\ \end{aligned}\)

      3.形式二\(\qquad if \quad f(n)=\sum_{n|d}g(d),then\quad g(n)=\sum_{n|d}f(d)\mu(\frac{d}{n})\)

      证明:

      \(\begin{aligned} \sum_{n|d}\mu(\frac{d}{n})f(d) &= \sum_{k=1}^{+\infty}\mu(k)f(kn)\\ &= \sum_{k=1}^{+\infty}\mu(k)\sum_{kn|d}^{+\infty}g(d)\\ &= \sum_{n|d}g(d)\sum_{k|\frac{d}{n}}\mu(k)\\ &= \sum_{n|d}g(d)\epsilon(\frac{d}{n})\\ &= g(n) \end{aligned}\)

    • 应用

      1. problem b

        题目大意:对于给出的\(n\)个询问,每次求有多少个数对\((x,y)\),满足 \(a≤x≤b,c≤y≤d\),且 \(gcd⁡(x,y)=k\)

        solution:

        直接求\(\sum_{i=a}^b\sum_{j=c}^d[\gcd(i,j)=k]\)不好求,考虑用简单的容斥原理转化为\(\sum_{i=1}^b\sum_{j=1}^d[\gcd(i,j)=k]-\sum_{i=1}^{a-1}\sum_{j=1}^{d}[\gcd(i,j)=k]-\sum_{i=1}^b\sum_{j=1}^{c-1}[\gcd(i,j)=k]+\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}[\gcd(i,j)=k]\)

        则只需要推\(\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\) (假设n\(\le\)m)

        \(\begin{aligned} \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k] &= \sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1]\\ &= \sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\\ &=\sum_{d=1}^n\mu(d)\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[d|i][d|j]\\ &= \sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor \end{aligned}\)

        后面的整除分块加\(\mu\)前缀和即可

        \(end\)

        code:

        const int N = 1e7+10;
        vector<int> prime;
        bitset<N> pd;
        int mu[N];
        inline void mobius(int n){
            mu[1] = 1;
            for(int i = 2;i <= n; ++i){
                if(!pd[i]) prime.emplace_back(i),mu[i] = -1;
                for(auto j : prime){
                    if(i*j > n) break;
                    pd[i*j] = true;
                    if(i%j == 0) break;
                    mu[i*j] = -mu[i];
                }
            }
            for(int i = 1;i <= n; ++i) mu[i] += mu[i-1];
        }
        inline int solve(int n,int m,int k){
            if(n > m) swap(n,m);
            int res = 0;
            for(int l = 1,r = 0;l <= n;l = r+1){
                r = min(n/(n/l),m/(m/l));
                res = res+(mu[r]-mu[l-1])*(n/(l*k))*(m/(l*k));
            }
            return res;
        }
        //main
        mobius(N-10);
        int T;read(T);
        while(T--){
            int a,b,c,d,k;read(a,b,c,d,k);
            write(solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k),'\n');
        }
        
      2. YY的gcd

        题目大意:\(给定 N,M,求 1≤x≤N,1≤y≤M 且 gcd⁡(x,y) 为质数的 (x,y) 有多少对。\)

        solution:

        先写出暴力式子\(\sum_{p\in\mathbb{{p}}}\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p]\)

        推倒

        \(\begin{aligned} \sum_{p\in\mathbb{{p}}}\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p] &= \sum_{p\in\mathbb{{p}}}\sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor}[\gcd(i,j)=1]\\ &= \sum_{p\in\mathbb{{p}}}\sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\\ &= \sum_{p\in\mathbb{{p}}}\sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor \end{aligned}\)

        (一个套路:)用\(T\)替换\(pd\)

        \(\begin{aligned} \sum_{p\in\mathbb{{p}}}\sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor = \sum_{T=1}^n\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\mu(d) \end{aligned}\)

        后面的\(\sum_{d|T}\mu(d)\)可以暴力预处理求

        \(end\)

        code:

        const int N = 1e7+10;
        vector<int> prime;
        bitset<N> pd;
        int mu[N];
        ll f[N];
        inline void mobius(int n){
            mu[1] = 1;
            for(int i = 2;i <= n; ++i){
                if(!pd[i]) prime.emplace_back(i),mu[i] = -1;
                for(auto j : prime){
                    if(i*j > n) break;
                    pd[i*j] = true;
                    if(i%j == 0) break;
                    mu[i*j] = -mu[i];
                }
            }
            for(auto i:prime){
                for(int j = 1;j*i <= n; ++j){
                    f[i*j] += mu[j];
                }
            }
            for(int i = 1;i <= n; ++i) f[i] += f[i-1];
        }
        inline ll solve(int n,int m){
            if(n > m) swap(n,m);
            ll res = 0;
            for(int l = 1,r = 0;l <= n;l = r+1){
                r = min(n/(n/l),m/(m/l));
                res = res+1ll*(f[r]-f[l-1])*(n/l)*(m/l);
            }
            return res;
        }
        //main
        mobius(N-10);
        int T;read(T);
        while(T--){
            int n,m;read(n,m);
            write(solve(n,m),'\n');
        }
        

      进阶:

      3.数表

      \(有一张 n×m的数表,其第 i 行第 j 列(1≤i≤n,1≤j≤m)的数值为能同时整除 i 和 j 的所有自然数之和。给定 a,计算数表中不大于 a 的数之和。\)

      多出来的\(\le a\)的限制很难评,那就先不考虑了

      推式子

      \(\begin{aligned} &\sum_{d=1}^n\sigma(d)\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=d]\\ &=\sum_{d=1}^n\sigma(d)\sum_{k=1}^n\mu(k)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor \end{aligned}\)

      \(let\quad T = kd\)

      \(\begin{aligned} &\sum_{T=1}^n\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\sigma(d)\mu(\frac{T}{d}) \end{aligned}\)

      后面明显的狄利克雷卷积,但因为那个\(\sigma(d)\le a\)的限制,所以就寄了。

      那就暴力? no,可以考虑离线,将询问按\(a\)升序排序,每次将符合条件的\(\sigma(d)\)插入一个BIT中,查询前缀和即可。

      \(end\)

      \(code:\)

      const int N = 1e5+10;
      vector<int> prime;
      bitset<N> pd;
      int mu[N];
      int s[N],low[N];
      struct node{int val,id;}a[N];
      struct Query{int n,m,a,id;}Q[N];
      uint ans[N];
      class BIT{
      private:
          int tree[N];
          inline int lowbit(int x){return (x&(-x));}
      public:
          int n = 0;
          inline void update(int pos,int val){for(int i = pos;i <= n;i += lowbit(i)) tree[i] += val;}
          inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
      }T;
      inline void mobius(int n){
          mu[1] = 1,s[1] = 1;
          for(int i = 2;i <= n; ++i){
              if(!pd[i]) prime.emplace_back(i),mu[i] = -1,s[i] = i+1,low[i] = i;
              for(auto j : prime){
                  if(i*j > n) break;
                  pd[i*j] = true;
                  if(i%j == 0){
                      low[i*j] = low[i]*j;
                      if(low[i] == i) s[i*j] = s[i]+i*j;
                      else s[i*j] = s[i/low[i]]*s[low[i]*j];
                      break;
                  }
                  low[i*j] = j;
                  s[i*j] = s[i]*s[j];
                  mu[i*j] = -mu[i];
              }
          }
          for(int i = 1;i <= n; ++i) a[i] = {s[i],i};
          sort(a+1,a+1+n,[](node x,node y){return x.val < y.val;});
      }
      inline int solve(int n,int m){
          int res = 0;
          for(int l = 1,r;l <= n;l = r+1){
              r = min(n/(n/l),m/(m/l));
              res = res + (T.query(r)-T.query(l-1))*(n/l)*(m/l);
          } 
          return res;
      }
      //main
      int t;read(t);
      int mx = 0;
      for(int i = 1;i <= t; ++i){
          int n,m,a;read(n,m,a);
          if(n > m) swap(n,m);
          T.n = max(T.n,n);
          mx = max(mx,n);
          Q[i]={n,m,a,i};
      }
      sort(Q+1,Q+1+t,[](Query x,Query y){return x.a<y.a;});
      mobius(mx);
      int now = 1;
      for(int i = 1;i <= t; ++i){
          int n = Q[i].n,m = Q[i].m,lim = Q[i].a;
          while(now<=mx&&a[now].val<=lim){
              for(int j = a[now].id;j <= mx;j += a[now].id) T.update(j,mu[j/a[now].id]*a[now].val);
              ++now;
          }
          ans[Q[i].id] = solve(n,m);
      }
      for(int i = 1;i <= t; ++i){
          write(ans[i]&2147483647,'\n');
      }
      

      \(to\quad be\quad continued\)

posted @ 2024-06-28 21:13  CuFeO4  阅读(13)  评论(0编辑  收藏  举报