Write in front
This is the first time konjaku has written such a long blog
\(gyh\ nb\),\(\text{OI-Wiki}\ nb\)
If you think it's OK to write, please click support \ (qwq \)
Prior knowledge
Product function,Dirichlet convolution,Number theory is divided into parts (go to gyh for this article, I can't talk about it well) (make up slowly when you have time)
Mobius function
Definition
The Mobius function \ (\ mu(n) \) is defined as:
Where \ (P 〝 1p 〝 2 , P (s \) is a different prime number.
It can be seen that \ (\ mu(n) \) is not zero when \ (n \) has no square factor.
\(\ mu \) is also a product function.
Nature
Mobius function has the following properties:
Using the Dirichlet convolution, i.e
prove:
\(n=1 \) obviously.
If \ (n > 1 \), set \ (n \) to have \ (s \) different prime factors. Because \ (\ mu(d)\neq0 \) is and only if \ (D \) has no square factor, the index of each prime factor in \ (D \) can only be \ (0 \) or \ (1 \).
If a factor \ (D \) of \ (n \) has \ (\ mu(d)=(-1)^i \), it is composed of \ (i \) different quality factors. Because the total number of quality factors i s \ (s \), there are \ (C ^ i \) factors satisfying the above formula.
So we can convert the original formula into enumeration \ (\ mu(d) \), and use binomial theorem at the same time
The formula is 1 when \ (s=0 \) is \ (n=1 \), otherwise \ (0 \).
Find Mobius function
Because Mobius function is a product function, linear sieve can be used
int n, cnt, p[A], mu[A]; bool vis[A]; void getmu() { mu[1] = 1; for (int i = 2; i <= n; i++) { if (!vis[i]) mu[i] = -1, p[++cnt] = i; for (int j = 1; j <= cnt && i * p[j] <= n; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) { mu[i * p[j]] = 0; break; } mu[i * p[j]] -= mu[i]; } } }
M bius inversion formula
Let \ (f(n) \) and \ (g(n) \) be two number theoretic functions.
If any
Then there are
Prove
-
Method 1: make number theory transformation to the original formula
-
\(\ sum \ limits {d| n} g (d) \) replace \ (f(n) \), i.e
\[\sum\limits_{d|n}\mu(d)f(\frac{n}{d})=\sum\limits_{d|n}\mu(d)\sum\limits_{k|\frac nd}g(k) \] -
Change the sum order to get
\[\sum\limits_{k|n}g(k)\sum\limits_{d|\frac n k}\mu(\frac nk) \] -
Because \ (\ sum \ limits {d| n} \ mu (d) = [n = 1] \), the above formula is equivalent to
\[\sum\limits_{d|n}[n=k]g(k)=g(n) \]
Obtain evidence
-
-
Method 2: using convolution
The original problem is: known \ (f=g*1 \), proof \ (g=f*\mu \)
Conversion: \ (f*\mu=g*1*\mu=g*\varepsilon=g \) (\ (1*\mu=\varepsilon \))
Re certification ==
Small property
\([\gcd(i,j)=1]\Leftrightarrow\sum\limits_{d|\gcd(i,j)}\mu(d)\)
Prove
-
Law 1:
If \ (n=\gcd(i,j) \), then \ (= \ sum \ limits {d| n} \ mu (d) = [n = 1] = [\ GCD (I, J) = 1] = \) on the right side of the equation
-
Method 2:
Using unit function violence to break apart: \ ([\ GCD (I, J) = 1] = \ varepsilon (\ GCD (I, J)) = \ sum \ limits {d| \ GCD (I, J)} \ mu (d) \)
Thinking of doing Questions & Application
A series of summation problems can be solved by using Dirichlet convolution. The common practice is to use a Dirichlet convolution to replace part of the summation formula, then change the order of summation, and ultimately reduce the time complexity.
The commonly used convolutions are \ (\ mu*1=\epsilon \) and \ (\ text{Id}=\varphi*1 \).
topic
It's better to focus on the questions, but the questions you do will also be written separately. After all, you need more blogs / huaji
Luogu P2522 [HAOI2011]Problem b
There are \ (n \) group queries. Each time \ (a,b,c,d,k \) is given, find \ (\ sum \ limits {x = a} ^ {B} \ sum \ limits {y = C} ^ {D} [\ GCD (x, y = k] \)
Set \ (f (n, m) = \ sum \ limits {I = 1} ^ {n} \ sum \ limits {J = 1} ^ {m} [\ GCD (I, j = k] \)
Then according to the principle of inclusion and exclusion, the formula in the topic is transformed into \ (f(b,d)-f(b, c - 1) - f(a - 1,d) + f(a - 1, c - 1) \)
So our next question is how to find the value of \ (f \)
Now simplify the value of \ (f \)
-
It is easy to get that the original formula is equivalent to $$\ sum \ limits {I = 1} ^ {\ lfloor \ frac {n} {K} \ rfloor} \ sum \ limits {J = 1} ^ {\ lfloor \ frac {m} {K} \ rfloor} [\ GCD (I, J) = 1]$$
-
Because \ (\ epsilon (n) = \ sum \ limits {d| n} \ mu (d) = [n = 1] \), the original formula can be changed to
\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum\limits_{d|gcd(i,j)}\mu(d) \] -
Change the enumeration object and change the enumeration order. First enumerate \ (d \), and get
\[\sum\limits_{d=1}^{\min(n,m)}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}[d|i]\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}[d|j] \]That is, when \ (d|i \) and \ (d|j \), \ (d|\gcd(i,j) \)
-
1} ^ {\ min (n, m)} \ mu (d) \ lfloor \ frac {n} {d k} \ rfloor \ lfloor\frac{m}{dk}\rfloor$$
At this time, it can be solved by \ (O(n) \), but it can't pass. Because there are many ranges with the same value, it can be done by number theory block
First, preprocess \ (\ mu \), and then divide them into blocks according to number theory, with complexity of \ (O(n+T\sqrt n) \)
My code scores metaphysics every time, looks at the mood of the evaluator, and suggests writing by myself
/* Author:loceaner */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 1e6 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int n, a, b, c, d, k, cnt, p[A], mu[A], sum[A]; bool vis[A]; void getmu() { int MAX = 50010; mu[1] = 1; for (int i = 2; i <= MAX; i++) { if (!vis[i]) mu[i] = -1, p[++cnt] = i; for (int j = 1; j <= cnt && i * p[j] <= MAX; j++) { vis[i * p[j]] = true; if (i % p[j] == 0) break; mu[i * p[j]] -= mu[i]; } } for (int i = 1; i <= MAX; i++) sum[i] = sum[i - 1] + mu[i]; } int work(int x, int y) { int ans = 0ll; int max = min(x, y); for (int l = 1, r; l <= max; l = r + 1) { r = min(x / (x / l), y / (y / l)); ans += (1ll * x / (l * k)) * (1ll * y / (l * k)) * 1ll * (sum[r] - sum[l - 1]); } return ans; } void solve() { a = read(), b = read(), c = read(), d = read(), k = read(); cout << work(b, d) - work(a - 1, d) - work(b, c - 1) + work(a - 1, c - 1) << '\n'; } signed main() { getmu(); int T = read(); while (T--) solve(); return 0; }
Luogu p3455 [poi2007] zap queries
There are \ (T \) group queries, each time
Because i don't like to use \ (x, y, a, b, d \), i'll replace them with \ (i, j, n, m, k \)
Direct Ganon
Now you can ask \ (O(n) \) to do this question every time
But we can't run, but obviously we can divide the number theory into blocks, so we can \ (O(\sqrt n) \) answer every question
/* Author:loceaner */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 5e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } int n, m, k, mu[A], p[A], sum[A], cnt; bool vis[A]; void getmu(int n) { mu[1] = 1; for (int i = 2; i <= n; i++) { if (!vis[i]) p[++cnt] = i, mu[i] = -1; for (int j = 1; j <= cnt && i * p[j] <= n; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) break; mu[i * p[j]] -= mu[i]; } } for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i]; } int solve(int n, int m, int k) { int ans = 0, maxn = min(n, m); for (int l = 1, r; l <= maxn; l = r + 1) { r = min(n / (n / l), m / (m / l)); ans += (sum[r] - sum[l - 1]) * (n / (k * l)) * (m / (k * l)); } return ans; } int main() { getmu(50000); int T = read(); while (T--) { n = read(), m = read(), k = read(); cout << solve(n, m, k) << '\n'; } return 0; }
Logue P1829 [national training team] Crash's digital form / JZPTAB
seek
It is easy to think that the original formula is equivalent to
Enumeration of the greatest common divisor \ (d \) of \ (i,j \), obviously \ (\ GCD (\ frac id \ frac jd) = 1 \), that is, \ (\ frac id \) and \ (\ frac jd \) are mutually prime
Transform sum order
Note \ (sum (n, m) = \ sum \ limits {I = 1} ^ {n} \ sum \ limits {J = 1} ^ {m} [\ GCD (I, J) = 1] I * J \)
Simplify it, replace \ ([\ gcd(i,j)=1] \) with \ (\ varepsilon(\gcd(i,j)) \)
Convert to enumerating divisor first
If \ (i=i'*d,j=j'*d \), it can be further converted
The first half can process the prefix sum, and the second half can \ (O(1) \). Set
Obviously, \ (O(1) \) can be solved
Up to now
It can be solved by number theory in blocks
Bring back to the original formula
It can be solved by number theory and block
And then it's done
/* Author:loceaner */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define int long long using namespace std; const int A = 1e7 + 11; const int B = 1e6 + 11; const int mod = 20101009; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } bool vis[A]; int n, m, mu[A], p[B], sum[A], cnt; void getmu() { mu[1] = 1; int k = min(n, m); for (int i = 2; i <= k; i++) { if (!vis[i]) p[++cnt] = i, mu[i] = -1; for (int j = 1; j <= cnt && i * p[j] <= k; ++j) { vis[i * p[j]] = 1; if (i % p[j] == 0) break; mu[i * p[j]] = -mu[i]; } } for (int i = 1; i <= k; i++) sum[i] = (sum[i - 1] + i * i % mod * mu[i]) % mod; } int Sum(int x, int y) { return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod; } int solve2(int x, int y) { int res = 0; for (int i = 1, j; i <= min(x, y); i = j + 1) { j = min(x / (x / i), y / (y / i)); res = (res + 1LL * (sum[j] - sum[i - 1] + mod) * Sum(x / i, y / i) % mod) % mod; } return res; } int solve(int x, int y) { int res = 0; for (int i = 1, j; i <= min(x, y); i = j + 1) { j = min(x / (x / i), y / (y / i)); res = (res + 1LL * (j - i + 1) * (i + j) / 2 % mod * solve2(x / i, y / i) % mod) % mod; } return res; } signed main() { n = read(), m = read(); getmu(); cout << solve(n, m) << '\n'; }
GCD of Logu P2257 YY
Given \ (n,m \), find the number of tuples \ ((x,y) \), satisfy \ (1\leq x\leq n,1\leq y\leq m \), and \ (gcd(x,y) \) is prime.
\(n,m\leq 10^7 \), with multiple groups of data, at most \ (10 ^ {4} \) groups of data.
The idea is similar to Problem B in the first question. I will not repeat it here, but only give the code ==
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int A = 1e7 + 11; inline int read() { char c = getchar(); int x = 0, f = 1; for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } bool vis[A]; long long sum[A]; int prim[A], mu[A], g[A], cnt, n, m; void get_mu(int n) { mu[1] = 1; for (int i = 2; i <= n; i++) { if (!vis[i]) { mu[i] = -1; prim[++cnt] = i; } for (int j = 1; j <= cnt && prim[j] * i <= n; j++) { vis[i * prim[j]] = 1; if (i % prim[j] == 0) break; else mu[prim[j] * i] = - mu[i]; } } for (int j = 1; j <= cnt; j++) for (int i = 1; i * prim[j] <= n; i++) g[i * prim[j]] += mu[i]; for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (long long)g[i]; } signed main() { int t = read(); get_mu(10000000); while (t--) { n = read(), m = read(); if (n > m) swap(n, m); long long ans = 0; for (int l = 1, r; l <= n; l = r + 1) { r = min(n / (n / l), m / (m / l)); ans += 1ll * (n / l) * (m / l) * (sum[r] - sum[l - 1]); } cout << ans << '\n'; } return 0; }
Luogu P3327 [SDOI2015] sum of approximate numbers
seek
\(d(x) \) is the sum of the divisors of \ (x \)
Need to use
Prove that I won't either
And then I'll deduce it myself. I won't repeat it here
/* Author:loceaner */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int A = 5e5 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } bool vis[A]; int n, m, p[A], mu[A], cnt, sum[A]; long long g[A], ans; void getmu(int n) { mu[1] = 1; for (int i = 2; i <= n; i++) { if (!vis[i]) mu[i] = -1, p[++cnt] = i; for (int j = 1; j <= cnt && i * p[j] <= n; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) break; mu[i * p[j]] -= mu[i]; } } for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i]; for (int i = 1; i <= n; i++) { int ans = 0; for (int l = 1, r; l <= i; l = r + 1) { r = (i / (i / l)); ans += 1ll * (r - l + 1) * (i / l); } g[i] = ans; } } signed main() { int T = read(); getmu(50000); while (T--) { n = read(), m = read(); int maxn = min(n, m); ans = 0; for (int l = 1, r; l <= maxn; l = r + 1) { r = min(n / (n / l), m / (m / l)); ans += 1ll * (sum[r] - sum[l - 1]) * 1ll * g[n / l] * 1ll * g[m / l]; } cout << ans << '\n'; } return 0; }
Logue P4449 in the wrath of God
seek
Or direct Gan
Let \ (P=dx \), then the original formula is equal to
Obviously, the preceding \ (\ lfloor\frac n{P}\rfloor\lfloor\frac m{P}\rfloor \) part can be solved in blocks.
Now consider the latter part, and make
It's easy to get that this function is a product function, so we can sift it linearly and find the prefix sum
And then it's done
/* Author:loceaner Mobius inversion */ #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define int long long using namespace std; const int A = 5e6 + 11; const int B = 1e6 + 11; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; inline int read() { char c = getchar(); int x = 0, f = 1; for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); return x * f; } bool vis[A]; int T, n, m, k, f[A], g[A], p[A], cnt, sum[A]; inline int power(int a, int b) { int res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod, b >>= 1; } return res; } inline int mo(int x) { if(x > mod) x -= mod; return x; } inline void work() { g[1] = 1; int maxn = 5e6 + 1; for (int i = 2; i <= maxn; i++) { if (!vis[i]) { p[++cnt] = i, f[cnt] = power(i, k), g[i] = mo(f[cnt] - 1 + mod); } for (int j = 1; j <= cnt && i * p[j] <= maxn; j++) { vis[i * p[j]] = 1; if (i % p[j] == 0) { g[i * p[j]] = g[i] * 1ll * f[j] % mod; break; } g[i * p[j]] = g[i] * 1ll * g[p[j]] % mod; } } for (int i = 2; i <= maxn; i++) g[i] = (g[i - 1] + g[i]) % mod; } inline int abss(int x) { while (x < 0) x += mod; return x; } signed main() { T = read(), k = read(); work(); while (T--) { n = read(), m = read(); int maxn = min(n, m), ans = 0; for (int l = 1, r; l <= maxn; l = r + 1) { r = min(n / (n / l), m / (m / l)); (ans += abss(g[r] - g[l - 1]) * 1ll * (n / l) % mod * (m / l) % mod) %= mod; } ans = (ans % mod + mod) % mod; cout << ans << '\n'; } return 0; }