莫比乌斯系列
莫比乌斯系列
莫比乌斯函数
定义
性质
Dirichlet卷积相关
平方相关
证明:考虑若 \(\mu(i)^2=1\),则右侧只有一项 \(j=1\)(因为 \(i\) 无平方因子)否则说明其至少有一个质因子 \(p\) 出现了两次以上。
考虑将所有 \(j^2\mid i\) 分成两类:\(p\) 是 \(j\) 的因子/不是 \(j\) 的因子(由于 \(\mu(j)\) 不能为 \(0\),因此 \(p\) 至多出现一次)。则两类 \(j\) 之间建立起一一对应关系,且对于每一对,它们的 \(\mu\) 互为相反数。因此这个 \(i\) 的贡献确实为 \(0\) 。
求法
单个数
inline int Mu(int n) {
int mu = 1;
for (int i = 2; i * i <= n; ++i)
if (!(n % i)) {
if (!(n % (i * i)))
return 0;
mu *= -1, n /= i;
}
if (n > 1)
mu *= -1;
return mu;
}
线性筛
inline void sieve(int n) {
memset(isp, true, sizeof(isp));
isp[1] = false, mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (isp[i])
pri[++pcnt] = i, mu[i] = -1;
for (int j = 1; j <= pcnt && i * pri[j] <= n; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
mu[i * pri[j]] = -mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
}
杜教筛
namespace Mu {
map<int, ll> mp;
ll f[N], sum[N];
inline void prework() {
for (int i = 1; i < N; ++i)
sum[i] = sum[i - 1] + f[i];
}
ll Sum(ll n) {
if (n < N)
return sum[n];
if (mp.find(n) != mp.end())
return mp[n];
ll res = 1;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= 1ll * (r - l + 1) * Sum(n / l);
}
return mp[n] = res;
}
} // namespace Mu
莫比乌斯反演
设 \(f(n), g(n)\) 为两个数论函数,由 \(\mu * 1 = \epsilon\) ,得到:
形式一
设 \(f(n), g(n)\) 为两个数论函数,则:
此时 \(f(n)\) 称为 \(g(n)\) 的莫比乌斯变换,\(g(n)\) 称为 \(f(n)\) 的莫比乌斯逆变换,即莫比乌斯反演。
形式二
设 \(f(n), g(n)\) 为两个数论函数,则:
常见应用
形式一
数论分块即可做到 \(O(n) - O(\sqrt{n})\) 。
形式二
令 \(d k = T\) ,则:
数论分块即可做到 \(O(n) - O(\sqrt{n})\) 。
扩展
对于数论函数 \(f, g\) 与完全积性函数 \(t\) 且满足 \(t(1) = 1\) ,则:
证明:
魔力筛
这是一个可以在 \(O(n\log\log n)\) 的时间复杂度求出任意数论函数与 \(\mu\) 的狄利克雷卷积的算法。当然,必须保证这个数论函数能被提前计算出来。
设 \(\mathbf g_{i,n}=\sum_{d\mid n}\mathbf f(d)\mu(\dfrac nd)\),其中 \(d\) 只含前 \(i\) 种质因子。则有:
需要滚动数组优化。
解释:第一种情况显然正确。对于第二种,由于每多一个质因子,\(\mu\) 的取值就会乘以 \(-1\) ,因此上式的意义是强制不选 \(p_i\) ,然后乘以 \(-1\) 累加在答案上。如果含有平方因子,则值为 \(0\) ,不影响答案。
复杂度和埃氏筛一样,为 \(O(n \log \log n)\) 。
inline void calc(int n) {
for (int i = 1; i <= n; ++i)
g[i] = f[i];
for (int p : prime)
for (int i = n / p; i; --i)
g[i * p] = g[i * p] - g[i];
}
应用
P2257 YY的GCD PGCD - Primes in GCD Table
求:
\[\sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) \in prime] \]\(n, m \leq 10^7\)
设 \(T = dk\) ,则:
\(n = m\) 的弱化版是这题:P2568 GCD
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e7 + 7;
ll f[N], sum[N];
int pri[N], mu[N];
bool isp[N];
int T, n, m, pcnt;
inline void sieve() {
memset(isp, true, sizeof(isp));
mu[1] = 1, isp[1] = false;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, mu[i] = -1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[pri[j] * i] = false;
if (i % pri[j])
mu[i * pri[j]] = -mu[i];
else
break;
}
}
for (int i = 1; i <= pcnt; ++i)
for (int j = 1; pri[i] * j < N; ++j)
f[j * pri[i]] += mu[j];
for (int i = 1; i < N; ++i)
sum[i] = sum[i - 1] + f[i];
}
signed main() {
sieve();
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
if (n > m)
swap(n, m);
ll ans = 0;
for (int l = 1, r = 0; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
}
printf("%lld\n", ans);
}
return 0;
}
设 \(d(n)\) 表示 \(n\) 的约数个数,求:
\[\sum_{i = 1}^n \sum_{j = 1}^m d(ij) \]\(T, n, m \leq 5 \times 10^4\)
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 5e4 + 7;
ll f[N];
int pri[N], mu[N], sum[N];
bool isp[N];
int T, n, m, pcnt;
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, mu[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, mu[i] = -1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
mu[i * pri[j]] = -mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
for (int i = 1; i < N; ++i)
sum[i] = sum[i - 1] + mu[i];
for (int i = 1; i < N; ++i)
for (int l = 1, r; l <= i; l = r + 1) {
r = i / (i / l);
f[i] += 1ll * (i / l) * (r - l + 1);
}
}
signed main() {
sieve();
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
if (n > m)
swap(n, m);
ll ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += f[n / l] * f[m / l] * (sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
return 0;
}
求:
\[\prod_{i = 1}^n \prod_{j = 1}^n \dfrac{\operatorname{lcm}(i, j)}{\gcd(i, j)} \pmod{104857601} \]\(n \leq 10^6\)
单独考虑下面的部分得到 :
单独考虑指数部分得到:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int Mod = 104857601;
const int N = 1e6 + 7;
int pri[N], phi[N], sum[N];
bool isp[N];
int n, pcnt;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline int mi(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = 1ll * a * a % Mod)
if (b & 1)
res = 1ll * res * a % Mod;
return res;
}
inline void sieve(int n) {
memset(isp, true, sizeof(isp));
isp[1] = false, phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (isp[i])
pri[++pcnt] = i, phi[i] = i - 1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 1; i <= n; ++i)
sum[i] = (sum[i - 1] + phi[i]) % (Mod - 1);
}
signed main() {
scanf("%d", &n);
sieve(n);
int fac = 1;
for (int i = 1; i <= n; ++i)
fac = 1ll * fac * i % Mod;
int ans = 1ll * mi(fac, n * 2) * fac % Mod * fac % Mod;
for (int i = 1; i <= n; ++i)
ans = 1ll * ans * mi(mi(i, 4ll * sum[n / i] % (Mod - 1)), Mod - 2) % Mod;
printf("%d", ans);
return 0;
}
求:
\[\sum_{i = 1}^{n} \sum_{j = 1}^n \sum_{p = 1}^{\lfloor \frac{n}{j} \rfloor} \sum_{q = 1}^{\lfloor \frac{n}{j} \rfloor} [\gcd(i, j) = 1] [\gcd(p, q) = 1] \pmod{998244353} \]\(n \leq 2 \times 10^9\)
杜教筛处理 \(\mu\) 的前缀和即可。
#include <bits/stdc++.h>
using namespace std;
const int Mod = 998244353;
const int N = 1e7 + 7;
map<int, int> mp;
int pri[N], mu[N], sum[N];
bool isp[N];
int n, pcnt;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, mu[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, mu[i] = Mod - 1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
mu[i * pri[j]] = Mod - mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
for (int i = 1; i < N; ++i)
sum[i] = add(sum[i - 1], mu[i]);
}
int Sum(int n) {
if (n < N)
return sum[n];
if (mp.find(n) != mp.end())
return mp[n];
int res = 1;
for (int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res = dec(res, 1ll * (r - l + 1) * Sum(n / l) % Mod);
}
return mp[n] = res;
}
signed main() {
sieve();
scanf("%d", &n);
int ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans = add(ans, 1ll * (n / l) * (n / l) % Mod * (n / l) % Mod * dec(Sum(r), Sum(l - 1)) % Mod);
}
printf("%d", ans);
return 0;
}
求:
\[\sum_{i = 1}^n \sum_{j = 1}^m \gcd(i, j)^k \pmod{10^9 + 7} \]\(n, m, k \leq 5 \times 10^6\)
线性筛 \(g(T) = \sum_{d | T} d^k \mu(\dfrac{T}{d})\) 即可。
#include <bits/stdc++.h>
using namespace std;
const int Mod = 1e9 + 7;
const int N = 5e6 + 7;
int pri[N], low[N], g[N], sum[N];
bool isp[N];
int T, n, m, k, pcnt;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline int mi(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = 1ll * a * a % Mod)
if (b & 1)
res = 1ll * res * a % Mod;
return res;
}
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, low[1] = 1, g[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i]) {
pri[++pcnt] = i, low[i] = i, g[i] = dec(mi(i, k), 1);
int x = i;
while (1ll * x * i < N)
x *= i, low[x] = x, g[x] = dec(mi(x, k), mi(x / i, k));
}
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
int x = i * pri[j];
isp[x] = false;
low[x] = i % pri[j] ? pri[j] : low[i] * pri[j];
g[x] = 1ll * g[x / low[x]] * g[low[x]] % Mod;
if (!(i % pri[j]))
break;
}
}
for (int i = 1; i < N; ++i)
sum[i] = add(sum[i - 1], g[i]);
}
signed main() {
scanf("%d%d", &T, &k);
sieve();
while (T--) {
scanf("%d%d", &n, &m);
int ans = 0;
if (n > m)
swap(n, m);
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans = add(ans, 1ll * (n / l) * (m / l) % Mod * dec(sum[r], sum[l - 1]) % Mod);
}
printf("%d\n", ans);
}
return 0;
}
求:
\[\sum_{i = 1}^n \sum_{j = 1}^n ij \gcd(i, j) \pmod{p} \]\(n \leq 10^{10}\)
其中 \(S(n) = \dfrac{n(n + 1)}{2}\) ,令 \(T = dk\) 则:
杜教筛处理 \(T^2 \varphi(T)\) 的前缀和即可,具体为构造 \(g(n) = n^2\) 。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e7 + 7;
map<ll, int> mp;
int pri[N], phi[N], sum[N];
bool isp[N];
ll n;
int Mod, pcnt, inv6;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline int mi(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = 1ll * a * a % Mod)
if (b & 1)
res = 1ll * res * a % Mod;
return res;
}
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, phi[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, phi[i] = i - 1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 1; i < N; ++i)
sum[i] = add(sum[i - 1], 1ll * i * i % Mod * phi[i] % Mod);
}
inline int Sum1(ll n) {
n %= Mod;
return n * (n + 1) / 2 % Mod;
}
inline int Sum2(ll n) {
n %= Mod;
return n * (n + 1) % Mod * (n * 2 + 1) % Mod * inv6 % Mod;
}
int Sum(ll n) {
if (n < N)
return sum[n];
if (mp.find(n) != mp.end())
return mp[n];
int res = 1ll * Sum1(n) * Sum1(n) % Mod;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res = dec(res, 1ll * dec(Sum2(r), Sum2(l - 1)) * Sum(n / l) % Mod);
}
return mp[n] = res;
}
signed main() {
scanf("%d%lld", &Mod, &n);
sieve();
inv6 = mi(6, Mod - 2);
int ans = 0;
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans = add(ans, 1ll * Sum1(n / l) * Sum1(n / l) % Mod * dec(Sum(r), Sum(l - 1)) % Mod);
}
printf("%d", ans);
return 0;
}
求:
\[\sum_{i_1 = L}^R \sum_{i_2 = L}^R \cdots \sum_{i_n = L}^R [\gcd(i_1, i_2, \cdots, i_n) = k] \pmod{10^9 + 7} \]\(n, k \leq 10^9\) ,\(L, R \leq 10^9\) ,\(R - L \leq 10^5\)
令 \(l = \lceil \dfrac{L}{k} \rceil, r = \lfloor \dfrac{R}{k} \rfloor\) ,则:
#include <bits/stdc++.h>
using namespace std;
const int Mod = 1e9 + 7;
const int N = 1e7 + 7;
map<int, int> mp;
int pri[N], mu[N], sum[N];
bool isp[N];
int n, k, L, R, pcnt;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline int mi(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = 1ll * a * a % Mod)
if (b & 1)
res = 1ll * res * a % Mod;
return res;
}
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, mu[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, mu[i] = Mod - 1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
mu[i * pri[j]] = Mod - mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
for (int i = 1; i < N; ++i)
sum[i] = add(sum[i - 1], mu[i]);
}
inline int Sum(int n) {
if (n < N)
return sum[n];
if (mp.find(n) != mp.end())
return mp[n];
int res = 1;
for (int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res = dec(res, 1ll * (r - l + 1) * Sum(n / l) % Mod);
}
return mp[n] = res;
}
signed main() {
sieve();
scanf("%d%d%d%d", &n, &k, &L, &R);
L = (L - 1) / k, R /= k;
int ans = 0;
for (int l = 1, r; l <= R; l = r + 1) {
r = R / (R / l);
if (L / l)
r = min(r, L / (L / l));
ans = add(ans, 1ll * mi(R / l - L / l, n) * dec(Sum(r), Sum(l - 1)) % Mod);
}
printf("%d", ans);
return 0;
}