莫比乌斯反演


前置知识

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

\[\mu(n) = \begin{cases}0&\text{n存在平方因子}\\(-1)^p&\text{p为n的质因子个数}\\1&\text{n = 1} \end{cases} \]

  • 单位元函数 \(\epsilon(n) = [n == 1]:\)

\[\epsilon(n) = \begin{cases}1&\text{n = 1}\\0&n\neq1\end{cases} \]

  • 欧拉函数 \(\phi(n) = \sum_{i = 1}^{n}[gcd(i, n) == 1]\)
  • 积性函数 \(g(xy) = g(x) * g(y)\)
  • 因数和函数 \(\sigma(n)=\sum_{d|n}^{n}d\)
  • 因数个数 \(d(n) = \sum_{d|n}^{n}1\)

直接结论

  • 莫比乌斯反演:

\[约数形式\qquad F(n) = \sum_{d\mid n}f(d)\ \ \Rightarrow\ \ f(n) = \sum_{d\mid n}\mu(d) \times F(\frac{n}{d}) \]

\[\color{#f97}{倍数形式}\qquad F(n) = \sum_{n\mid d}f(d)\ \ \Rightarrow\ \ f(n) = \sum_{n\mid d}\mu(\frac{d}{n}) \times F(d) \]

  • 反演加二项式得出结论:

\[\sum_{d|n}\mu(d) = \epsilon(n) = [n == 1] \]

  • 有关euler函数的结论:

\[\sum_{d|n}\phi(d) = n \]

  • 有关d函数的结论:

\[d(ij)=\sum_{x|i}^{n}\sum_{y|j}^{m}[gcd(x,y)=1]\rightarrow\sum_{d|gcd(i,j)}\mu(d)\sum_{d|x\ \& \ x|i}\sum_{d|y\ \& \ y|j}\rightarrow\sum_{d|gcd(i,j)}\mu(d)\times d(\lfloor\frac{i}{d}\rfloor)\times d(\lfloor\frac{j}{d}\rfloor) \]

简单的几个莫比乌斯变化:

1.

\[\sum_{i = 1}^n\sum_{j = 1}^m[gcd(i,j) == d] \rightarrow \sum_{d}^{n}\mu(d)\times\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor \]

  • 证明

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

    \[{莫比乌斯变换}\qquad\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)}^n\mu(d) \]

    \[{改为枚举d}\qquad\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{m}\sum_{j=1}^{n}\ [d|i]\times[d|j] \]

    \[{消去后面两个求和}\qquad\sum_{d = 1}^{n}\mu(d)\times\lfloor\frac{n}{d}\rfloor\times\lfloor\frac{m}{d}\rfloor \]

  • 应用

    [POI2007] ZAP-Queries - 洛谷

    根据题意,直接套用,做一个莫比乌斯变换

    \[\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)=d]\ \Rightarrow\ \sum_{d=1}^n\mu(d)\times\lfloor\frac{a}{d}\rfloor\times\lfloor\frac{b}{d}\rfloor \]

    这样就可以了, 当然没有,这题多组样例,直接枚举时间复杂度显然过不去,观察后面两个式子发现,显然可以用数论分块优化,显然,\(\lfloor\frac{a}{d}\rfloor\)相同的所有d可以在一起计算

    所以我们要锁定一个块的右端点,左端点为 L , 右端点为 R,可以知道,我们要找到一个最大的 R 使得\(\frac{a}{L}\le\frac{a}{R}\rightarrow R \le a /(a/L)\), 那么就结束了

    • \(\it{Code}\)

      #include <bits/stdc++.h>
      
      const int mod = 998244353;
      const int N = 2e5 + 10;
      using namespace std;
      
      #define int long long
      
      int ph1[N];
      bool st[N];
      vector<int> primes;
      int mu[N];
      
      void Mobius(int n) {
          ph1[1] = 1;
          mu[1] = 1;
          for (int i = 2; i <= n; i++) {
              if (!st[i]) {
                  ph1[i] = i - 1;
                  mu[i] = -1;
                  primes.push_back(i);
              }
              for (auto j : primes) {
                  if (j * i > n) break;
                  st[j * i] = true;
                  if (i % j == 0) {
                      ph1[j * i] = ph1[i] * j;
                      mu[j * i] = 0;
                      break;
                  }
                  ph1[j * i] = ph1[i] * (j - 1);
                  mu[j * i] = mu[i] * -1;
              }
          }
      }
      
      int sum_mu[N];
      
      void init_sum_mu(int n) {
          for (int i = 1; i <= n; i++) {
              sum_mu[i] = sum_mu[i - 1] + mu[i];
          }
      }
      
      int calc(int a, int b, int d) {
          a /= d;
          b /= d;
          int n = min(a, b);
          int ans = 0;
          for (int l = 1, r; l <= n; l = r + 1) {
              r = min(n, min(a / (a / l), b / (b / l)));
              ans += (sum_mu[r] - sum_mu[l - 1]) * (a / l) * (b / l);
          }
          return ans;
      }
      
      signed main() {
          ios::sync_with_stdio(false);
          cin.tie(nullptr);
          Mobius(N - 1);
          init_sum_mu(N - 1);
          int T;
          cin >> T;
          while (T--) {
              int a, b, d;
              cin >> a >> b >> d;
              cout << calc(a, b, d) << endl;
          }
      }
      
      

2.

\[\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor\sum_{d|T} \mu(\frac{T}{d})\times d\quad \]

  • 证明

    \[首先化简\qquad \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d=1}^{n}[gcd(i,j)=d]\times d \]

    \[莫比乌斯变换 \qquad \sum_{d = 1}^{n} d\times \sum_{i = 1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}} [gcd(i,j)=1]\xlongequal{第一个公式化简}\sum_{d=1}^{n}d\times\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\mu(x)\times\lfloor\frac{n}{xd}\rfloor\times\lfloor\frac{m}{xd}\rfloor \]

    但是这样时间复杂度太大,会T掉,那就要想怎么去优化了,要想办法把其中的一些东西预处理掉

    优化复杂度,设\(xd=T\),全部代换掉,这样你就惊奇的发现,后面的这坨东西可以预处理

    \[ \sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor\sum_{d|T} d\times\mu(\frac{T}{d}) \]

  • 应用

    [SDOI2014] 数表 - 洛谷

    \[枚举因数\qquad\sum_{i=1}^{n}\sum_{j=1}^{m}\sigma(gcd(i, j))\Rightarrow\sum_{d = 1}^{n}\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor} \sigma(d) [gcd(i , j) = 1] \]

    \[莫比乌斯变换\qquad\sum_{d = 1}^{n}\sigma(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{x|i\&x|j}\mu(x)\Rightarrow\sum_{d = 1}^{n}\sigma(d)\sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x ) \times\lfloor\frac{n}{xd}\rfloor\times\lfloor\frac{m}{xd}\rfloor \]

    按照套路,\(T=xd\),带入计算

    \[\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\times\lfloor\frac{m}{T}\rfloor\sum_{d|T}\sigma(d)\times\mu(\frac{T}{d}) \]

    可以预处理出后面那一坨东西,这样就可以降低时间复杂度

3.

\[d(ij)=\sum_{x|i}^{n}\sum_{y|j}^{m}[gcd(x,y)=1]\rightarrow\sum_{d|gcd(i,j)}\mu(d)\times d(\lfloor\frac{i}{d}\rfloor)\times d(\lfloor\frac{j}{d}\rfloor) \]

  • 证明

    按照套路,先莫比乌斯变换

    \[\sum_{d|gcd(i,j)}\mu(d)\sum_{d|x\ \& \ x|i}\sum_{d|y\ \& \ y|j}1 \]

    然后考虑,后面所求的就是都多少个x即使i和j的因子,又是d的倍数那就就相当于x是\(\lfloor\frac{i}{d}\rfloor\)的因子,所以个数就是\(d({\lfloor\frac{i}{d}\rfloor})\)

    \[\sum_{d|gcd(i,j)}\mu(d)\sum_{d|x\ \& \ x|i}\sum_{d|y\ \& \ y|j}\rightarrow\sum_{d|gcd(i,j)}\mu(d)\times d(\lfloor\frac{i}{d}\rfloor)\times d(\lfloor\frac{j}{d}\rfloor) \]

  • 应用

    前两天刚好比赛做到一个莫比乌斯的题,可惜当时没写出来

    ZJNU终极赛 - Virtual Judge

    题意很简单就是让你推式子

    首先看到指数上的\(d(i,j)\),就先用这个公式给他化成枚举因子的形式

    \[\prod_{i=1}^{N} \prod_{j=1}^{N} ij^{d(i,j)} \Rightarrow\prod_{i=1}^{N} \prod_{j=1}^{N} ij^{\sum_{d|gcd(i,j)}\mu(d)\times d(\lfloor\frac{i}{d}\rfloor)\times d(\lfloor\frac{j}{d}\rfloor)} \]

    我们的目的还是打算把因数放到最外面来枚举,插播一条冷知识热知识,指数上的\(\sum\)放到底数上变成\(\prod\),所以我们可以利用这个

    \[\prod_{d=1}^{N}\prod_{i=1}^{N} \prod_{j=1}^{N} ij^{\mu(d)\times d(\lfloor\frac{i}{d}\rfloor)\times d(\lfloor\frac{j}{d}\rfloor)} \Rightarrow\prod_{d=1}^{N}\prod_{i=1}^{\lfloor\frac{N}{d}\rfloor} \prod_{j=1}^{\lfloor\frac{N}{d}\rfloor} ij^{\mu(d)\times d(i)\times d(j)} d^{2\times\mu(d)\times d(i)\times d(j)} \]

    接着,我们发现 \(i\)\(j\) 都是枚举N,那么很容易可以得到,\(i 和j的指数相同\),所以最后只需要将 \(i\) 平方一下就可以了,

    \[\prod_{d=1}^{N}\left((\prod_{i=1}^{\lfloor\frac{N}{d}\rfloor}i^{\sum_{j=1}^{\lfloor\frac{N}{d}\rfloor}d(\lfloor\frac{j}{d}\rfloor)})^2\times d^{2\times(\sum_{j=1}^{\lfloor\frac{N}{d}\rfloor}d(\lfloor\frac{j}{d}\rfloor))^{2}}\right)^{\mu(d)} \]

    对,就是这一坨恶心的东西,他其实不难求,很容易发现,你只要先求出 \(d\) 函数,然后再求出\(\sum_{j=1}^{\lfloor\frac{N}{d}\rfloor}d(\lfloor\frac{j}{d}\rfloor)\),然后最后你只需要枚举 \(d\) 就可以了,

    这样复杂度就限制在了预处理\(\prod_{i=1}^{\lfloor\frac{N}{d}\rfloor}i^{\sum_{j=1}^{\lfloor\frac{N}{d}\rfloor}d(\lfloor\frac{j}{d}\rfloor)}\)\(\sum_{j=1}^{\lfloor\frac{N}{d}\rfloor}d(\lfloor\frac{j}{d}\rfloor)\)这个东西上面,然后记得利用费马小定理对指数做 \(\%(MOD-1)\)

    • \(\it{Code}\)

      #include <bits/stdc++.h>
      
      using i64 = long long;
      const int N = 1e6 + 10;
      using namespace std;
      
      int MOD;
      
      int mu[N];
      bool st[N];
      vector<int> primes;
      void sieve(int n) {
        mu[1] = 1;
        for (int i = 2; i < n; i++) {
          if (!st[i]) {
            mu[i] = -1;
            primes.push_back(i);
          }
          for (auto j : primes) {
            if (j * i > n) break;
            st[j * i] = true;
            if (i % j == 0) break;
            mu[i * j] = -mu[i];
          }
        }
      }
      
      i64 qp(i64 a, int b, int p){
      	i64 res = 1;
      	while(b){
      		if (b & 1) res =res * a % p;
      		a = a * a % p;
      		b >>= 1;
      	}
      	return res;
      }
      
      void solve() {
        int n;
        cin >> n >> MOD;
        sieve(N - 1);
        vector<int> D(n + 1);
        vector<i64> Sum_D(n + 1);
        vector<i64> I(n + 1);
        I[0] = 1;
        for (int i = 1; i <= n; i++) {
          for (int j = i; j <= n; j += i) {
            D[j]++;
          }
          D[i] %= MOD - 1;
          Sum_D[i] = (Sum_D[i - 1] + D[i]) % (MOD - 1);
        	I[i] = qp(i, D[i], MOD);
        	I[i] = I[i - 1] * I[i] % MOD;
        }
        i64 Ans = 1;
        for (int i = 1; i <= n; i++) {
          i64 r = qp(I[n / i], Sum_D[n / i], MOD);
          r = r * r % MOD;
          r = r * qp(1LL * i * i % MOD, Sum_D[n / i] * Sum_D[n / i] % (MOD - 1), MOD) % MOD;
          if (mu[i] == -1) r = qp(r, MOD - 2, MOD);
          else if (mu[i] == 0) r = 1;
          Ans = Ans * r % MOD;
        }
        cout << Ans << endl;
      }
      
      signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int T = 1;
        // cin >> T;
        while (T--) {
          solve();
        }
        return 0;
      }
      
posted @ 2024-08-22 15:06  taijinvjin  阅读(95)  评论(0)    收藏  举报