莫比乌斯反演
前置知识
- 莫比乌斯函数 \(\mu(n)\) :
- 单位元函数 \(\epsilon(n) = [n == 1]:\)
- 欧拉函数 \(\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\)
直接结论
- 莫比乌斯反演:
- 反演加二项式得出结论:
- 有关euler函数的结论:
- 有关d函数的结论:
简单的几个莫比乌斯变化:
1.
-
证明
\[\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 \] -
应用
根据题意,直接套用,做一个莫比乌斯变换
\[\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.
-
证明
\[首先化简\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}) \] -
应用
\[枚举因数\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.
-
证明
按照套路,先莫比乌斯变换
\[\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) \] -
应用
前两天刚好比赛做到一个莫比乌斯的题,可惜当时没写出来
题意很简单就是让你推式子
首先看到指数上的\(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; }
-

浙公网安备 33010602011771号