莫比乌斯反演

莫比乌斯反演

引入

莫比乌斯反演用处:对于一些函数 f(n),如果比较难以求出它的值,但容易求出其倍数和或约束和 g(n),则可以通过莫比乌斯反演简化运算。

莫比乌斯函数

定义

定义 μ 为莫比乌斯函数,

μ(n)={1n=10n(1)kkn

详细解释一下后两条,我们令 n=i=1kpici,也就是将 n 分解质因数,pi 为质因子,ci1,则凡是有 ci>1μ(n)=0,而当任意的 ci 都等于 1μ(n)=(1)k

性质

d|nμ(d)={1n=10n1

证明

既然 n=i=1kpici ,那么我们令 d=i=1kpiβi,其中 0βici

对于存在 βi>=2 的情况,μ(d)=0,我们可以不管。

对于所有 βi 都小于等于 1 的情况,很显然我们按照βi=1有几个给它分一下组,也就是按照选了几个质因数给它分组,这样,很显然 μ(d) 的值是 Ck0×(1)0+Ck1×(1)1++Ckk×(1)k 也就是 i=0kCki×(1)i

看到这个形式,我们可以想起来二项式定理:

(a+b)k=Ck0akb0+Ck1ak1b1++Ckka0bk

我们可以令 a=1,b=1,代入二项式定理正好就是我们刚才推出的式子。

所以

d|nμ(d)=i=0kCki×(1)i=(11)k=0

莫比乌斯反演

形式一

定义在正整数域上的两个函数,若

F(n)=d|nf(d)

f(n)=d|nμ(d)F(nd)

莫比乌斯反演相关的题都是去套用这个定理来简化 f(n) 的计算。

证明

首先将 第一个式子代入第二个式子,消去 F(n):

d|nμ(d)F(nd)=d|nμ(d)i|ndf(i)

这其实就相当于一个二重循环:

for d|n
    for i|(n/d) 
        sum += mu(d) * f(i)

考虑将循环顺序颠倒,没有影响,i 可遍历到 n 的任何因数。

再考虑对于 μ(n) 的遍历,既然 i|nd,那么 di|n,那么 d|ni

则:

d|nμ(d)F(nd)=d|nμ(d)i|ndf(i)=i|nf(i)d|niμ(d)

前面已经证明过 d|nμ(d)={1n=10n1,则当只有 ni=1 时才会对答案有贡献,其他时候都是 0,则 i=n 时,答案为 f(n),证毕。

形式二

我们一般会用到莫比乌斯反演的另外一种形式:

F(n)=n|df(d)f(n)=n|dμ(dn)F(d)

证明

形式二的证明与形式一略有不同,但是大致一样。

首先依旧是将第一个式子代入第二个式子。

n|dμ(dn)F(d)=n|dμ(dn)d|if(i)

d=dnd=dn,因为 d|i,所以 dn|i,所以 d|in

n|dμ(dn)F(d)=n|dμ(dn)d|if(i)=n|if(i)d|inμ(d)

与前面同理,μ(n) 只有在 n=1 时才是 1,所以最终答案为 f(n).

例题

[HAOI2011]Problem B

#include <bits/stdc++.h>
#define int long long
#define maxn 50005
using namespace std;
const int INF = 1e9;
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
} 
int primes[maxn], cnt, mu[maxn], sum[maxn];
bool st[maxn];
void init() {
    mu[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!st[i]) primes[cnt++] = i, mu[i] = -1;
        for (int j = 0; primes[j] * i < maxn; j++) {
            st[primes[j] * i] = 1;
            if (i % primes[j] == 0) {
                mu[primes[j] * i] = 0;
                break;
            }
            mu[primes[j] * i] = -mu[i];
        }
    }
    for (int i = 1; i < maxn; i++) sum[i] = sum[i - 1] + mu[i];
}
int g(int k, int x) {
    return k / (k / x);
}
int f(int a, int b, int k) {
    a = a / k, b = b / k;
    int res = 0;
    int n = min(a, b);
    for (int l = 1, r; l <= n; l = r + 1) {
        r = min(n, min(g(a, l), g(b, l)));
        res += (sum[r] - sum[l - 1]) * (a / l) * (b/ l);
    }
    return res;
}
signed main() {
    init();
	int T = read();
    while (T--) {
        int a = read(), b = read(), c = read(), d = read(), k = read();
        cout << f(b, d, k) - f(a - 1, d, k) - f(b, c - 1, k) + f(a - 1, c - 1, k) << endl;
    }
}

[SDOI2015]约数个数和

#include <bits/stdc++.h>
#define int long long
#define maxn 50005
using namespace std;
const int INF = 1e9;
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
} 

int primes[maxn], cnt, mu[maxn], sum[maxn], h[maxn];
bool st[maxn];

void init() {
    mu[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!st[i]) primes[cnt++] = i, mu[i] = -1;
        for (int j = 0; primes[j] * i < maxn; j++) {
            st[primes[j] * i] = 1;
            if (i % primes[j] == 0) {
                mu[primes[j] * i] = 0;
                break;
            }
            mu[primes[j] * i] = -mu[i];
        }
    }
    for (int i = 1; i < maxn; i++) sum[i] = sum[i - 1] + mu[i];
    for (int i = 1; i < maxn; i++) {
        for (int l = 1, r; l <= i; l = r + 1) {
            r = min(i, i / (i / l));
            h[i] += (r - l + 1) * (i / l);
        }
    }
}

int g(int k, int x) {
    return k / (k / x);
}

signed main() {
    init();
	int T = read();
    while (T--) {
        int n = read(), m = read();
        int ans = 0, k = min(n, m);
        for (int l = 1, r; l <= k; l = r + 1) {
            r = min(k, min(n / (n / l), m / (m / l)));
            ans += (sum[r] - sum[l - 1]) * h[n / l] * h[m / l];
        }
        cout << ans << endl;
    }
}
/*
dij = sum(x|i)sum(y|j)gcd(x, y)==1
sumsumsumsum{gcd(x, y)==1}
F(n) = sumsumsumsum{n | gcd(x,y)}
     = sumsum(N/x)*(M/y)*(n | gcd(x, y))
    x' = N / x, y' = M / y --> F(n)
F(n) = sum(1->N/x)sum(1->M/y)(N'/x')(M'/y')
*/

VLATTICE - Visible Lattice Points

#include <bits/stdc++.h>
#define int long long
#define maxn 50005
using namespace std;
const int INF = 1e9;
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
} 

int primes[maxn], cnt, mu[maxn], sum[maxn];
bool st[maxn];

void init() {
    mu[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!st[i]) primes[cnt++] = i, mu[i] = -1;
        for (int j = 0; primes[j] * i < maxn; j++) {
            st[primes[j] * i] = 1;
            if (i % primes[j] == 0) {
                mu[primes[j] * i] = 0;
                break;
            }
            mu[primes[j] * i] = -mu[i];
        }
    }
    for (int i = 1; i < maxn; i++) sum[i] = sum[i - 1] + mu[i];
}

int g(int k, int x) {
    return k / (k / x);
}


signed main() {
    init();
	int T = read();
    while (T--) {
        int n = read();
        int ans = 0;
        for (int l = 1, r; l <= n; l = r + 1) {
            r = g(n, l);
            int t = n / l;
            ans += (sum[r] - sum[l - 1]) * (t * t * (3 + t));

        }
        ans += 3;
        cout << ans << endl;
    }
}
posted @   djc01  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示