莫比乌斯反演
2026-08-12 12:39:17
发布于:广东
听起来很难,但其实跟着lyy仔细推一推感觉也还好的。。
莫比乌斯函数:
令,则:
狄利克雷卷积
定义两个函数和的卷积为,则:
其具有的性质有:
1.交换律:
2.结合律:
3.分配律:
一些函数
1.,又称单位函数。
2.,又称常数函数。
3.
一些卷积恒等式
1.,即
证明:
令,,显然。
接着考虑贡献,这就是一个组合数的问题,每个质因子选或不选:
而且显然当时总和才会为
2.,证明:
按照定义带入:看起来证明很复杂?其实我也不会,好吧,感觉思路听巧妙的。。
将所有分数:化为最简分数,对于每个做分母,恰好有一个使得,所以有个,得证。
3.,证明:
由,将式子同时卷积上,则:
得证。
当然还有更多,大部分用的少,有用到的在补吧。。
莫比乌斯反演
形式1:若,即,则有:
证明用卷积很好证,这就不给了。
形式2:若,则有:
这个证明挺难的,反正我不会,谁会踢我一下。。
线性筛求和
按照定义来就行,直接贴代码吧。
mu[1] = 1;
for (int i = 2; i < N ; ++i) {
if (!is[i]) {
primes[++ cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * primes[j] < N; ++j) {
is[i * primes[j]] = true;
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
phi[1] = 1;
for (int i = 2; i < N ; ++i) {
if (!is[i]) {
primes[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && i * primes[j] < N ; ++j) {
is[i * primes[j]] = true;
if (i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
核心应用
考虑基础问题,用这个做一个引入:
我们可以将一个正方形分成两个三角形,每个三角形中一个数,与互质的数恰好是有个,当然单独考虑。
所以:
当然这道题中你还要考虑的两个情况。
那当:
显然刚刚的做法不可做了,那怎么办?
我们钦定
考虑对做转化:
所以:
显然可以交换求和顺序:
显然后面的那个东西可以用整除分块做,这样时间复杂度能做到级别。
拓展1:给定,
显然将提出去,和间有和个的倍数(就跟上面的转化一样),变为:
套板子即可。
拓展2:求。
根据得,即可做完,可以自己推一下。
P2522 [HAOI2011] Problem b
显然一个容斥原理,画个图你们就明白了:

当然上下界自己想清楚。。。
#include <bits/stdc++.h>
using namespace std;
namespace CZW {
#define endl "\n"
#define vec std::vector
#define pb push_back
#define eb emplace_back
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
constexpr int N = 5e4 + 5;
int primes[N], mu[N], cnt;
ll sum[N];
bool is[N];
void init() {
// freopen("1.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
mu[1] = 1;
for (int i = 2; i < N ; ++i) {
if (!is[i]) {
primes[++ cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * primes[j] < N ; ++j) {
is[i * primes[j]] = true;
if (i % primes[j] == 0){
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
for (int i = 1; i < N; ++i) sum[i] = sum[i - 1] + mu[i];
}
ll solve(int n, int m, int k){
n /= k, m /= k;
if (n > m) swap(n, m);
int l = 1, r = 0;
ll ans = 0;
while (l <= n){
r = min({n, n / (n / l), m / (m / l)});
ans += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
l = r + 1;
}
return ans;
}
void Main() {
int a, b, c, d, k;
cin >> a >> b >> c >> d >> k;
cout << solve(b, d, k) - solve(a - 1, d, k) - solve(b, c - 1, k) + solve(a - 1, c - 1, k) << endl;
}
}
int main() {
CZW::init();
int Test = 1;
cin >> Test;
while (Test--)
CZW::Main();
return 0;
}
GCD
一道非常幽默的题,题解区都是欧拉函数的做法,但显然这也是一个莫比乌斯反演的板子,枚举质数,跑一个板子即可。
但你们可能想问复杂度不是吗,不是过不了吗?
但是你们想一下,每次枚举质数,单次复杂度是的,算一下:
总复杂度:
根据素数定理和积分估计:
所以总复杂度:
显然稳过,代码不放了,板子来的。
Crash数字表格
非常爽的推柿子,我还是太蒻了,推一半卡住了,还要别人提醒我一下。
求。
令
令,显然可以算。
再令,显然对这个东西当每块定值,进行整除分块。
最后再对整体来一个整除分块即可。
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <unordered_map>
#include <set>
//#include <bits/stdc++.h>
using namespace std;
namespace CZW {
#define endl "\n"
#define vec std::vector
#define pb push_back
#define eb emplace_back
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
constexpr ll mod = 20101009, N = 1e7 + 5, inv2 = (mod + 1) / 2;
int primes[N], mu[N], cnt;
ll sum[N];
bool is[N];
void init() {
// freopen("1.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
mu[1] = 1;
for (int i = 2; i < N ; ++i) {
if (!is[i]) {
primes[++ cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * primes[j] < N; ++j) {
is[i * primes[j]] = true;
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
for (int i = 1; i < N; ++i) sum[i] = (sum[i - 1] + mu[i] * i % mod * i % mod) % mod;
// for (int i = 1; i < 1000; ++i) cout << mu[i] << ' ' << sum[i] << endl;
}
void Main() {
auto f1 = [&](int n, int m)->ll{
return 1ll * n * (n + 1) % mod * inv2 % mod * m % mod * (m + 1) % mod * inv2 % mod;
};
auto f2 = [&](int n, int m)->ll{
if (n > m) swap(n, m);
int l = 1, r = 0;
ll ans = 0;
while (l <= n) {
r = min({n, n / (n / l), m / (m / l)});
ans = (ans + ((sum[r] - sum[l - 1]) % mod + mod) % mod * f1(n / l, m / l) % mod) % mod;
l = r + 1;
}
return ans;
};
auto f3 = [&](int n, int m)->ll{
if (n > m) swap(n, m);
int l = 1, r = 0;
ll ans = 0;
while (l <= n) {
r = min({n, n / (n / l), m / (m / l)});
ans = (ans + f2(n / l, m / l) * (l + r) % mod * (r - l + 1) % mod * inv2 % mod) % mod;
// cout << ans << endl;
l = r + 1;
}
return ans;
};
// cout << f1(5, 6) << endl;
int n, m;
cin >> n >> m;
cout << f3(n, m);
}
}
int main() {
CZW::init();
int Test = 1;
// cin >> Test;
while (Test--)
CZW::Main();
return 0;
}
约数和
想做出这道题,有一个非常重要的结论,为积性函数。
所以对于,枚举和的因子,若则其必然为 的一个答案贡献,即:
既然这样很显然就可以开始推柿子了(建议自己推一下)
令,显然这个东西可以预处理。
然后还是对后面的东西进行整除分块即可。
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <unordered_map>
#include <set>
//#include <bits/stdc++.h>
using namespace std;
namespace CZW {
#define endl "\n"
#define vec std::vector
#define pb push_back
#define eb emplace_back
using ll = long long;
using ull = unsigned long long;
using i128 = __int128;
constexpr int N = 5e4 + 5;
int primes[N], mu[N], cnt;
bool is[N];
ll f[N], sum[N];
void init() {
// freopen("1.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
mu[1] = 1;
for (int i = 2; i < N; ++i) {
if (!is[i]) {
primes[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i * primes[j] < N; ++j) {
is[i * primes[j]] = true;
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
for (int i = 1; i < N; ++i) sum[i] = sum[i - 1] + mu[i];
auto get = [&](int n)->ll{
int l = 1, r = 0;
ll ans = 0;
while (l <= n) {
r = min(n, n / (n / l));
ans += 1ll * (r - l + 1) * (n / l);
l = r + 1;
}
return ans;
};
for (int i = 1; i < N; ++i) {
f[i] = get(i);
}
// for (int i = 1; i <= 10; ++i) cout << f[i] << ' ';
// cout << endl;
}
void Main() {
int n, m;
cin >> n >> m;
if (n > m) swap(n, m);
int l = 1, r = 0;
ll ans = 0;
while (l <= n) {
r = min({n, n / (n / l), m / (m / l)});
ans += (sum[r] - sum[l - 1]) * f[n / l] * f[m / l];
l = r + 1;
}
cout << ans << endl;
}
}
int main() {
CZW::init();
int Test = 1;
cin >> Test;
while (Test--)
CZW::Main();
return 0;
}
全部评论 7
- 置顶
怎么是,唐氏莫反
1周前 来自 广东
1数论的一生之敌
1周前 来自 浙江
1绷不住了
1周前 来自 广东
0
您怎么这么强!
1周前 来自 上海
1wu
1周前 来自 广东
0
1周前 来自 浙江
1?
4天前 来自 北京
0
不要更新BSGS
6天前 来自 浙江
0我学不会,,,
6天前 来自 浙江
0我好弱
6天前 来自 浙江
0
,,,
6天前 来自 浙江
0@cjdst@wcqk@yezi@Xylophone
@prediction
欢迎来完成莫比乌斯反演大学习!

1周前 来自 广东
0orz您怎么这么强
1周前 来自 浙江
1wu
1周前 来自 广东
0您咋这强
1周前 来自 浙江
0
ddddddd
1周前 来自 广东
0




























有帮助,赞一个