【算法&题解】莫比乌斯反演及一些经典套路

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/89219146

首先声明,由于我太菜了,本文很多式子可能并不会有详细的证明。若要了解莫比乌斯反演最基本的证明,可以去这里,本文主要讲解一些套路。

莫比乌斯函数

我们定义莫比乌斯函数如下:
n = i = 1 m p i a i , k = i = 1 m a i μ ( n ) = { 1 ( n = 1 ) ( 1 ) m ( k = 1 ) 0 ( k > 1 ) 设n=\prod_{i=1}^{m} p_i^{a_i},k=\prod_{i=1}^{m} a_i \\ \mu(n)= \begin{cases} 1 & (n=1) \\ (-1)^{m} & (k=1) \\ 0 & (k>1) \end{cases}
然后我们会发现它是一个积性函数,可以用线性筛 O ( n ) O(n)​ 求出 1 n 1-n​ 的莫比乌斯函数。代码如下:

mu[1] = 1;
for(int i = 2; i <= n; i++){
    if(!mark[i]){
        p[++cnt] = i;
        mu[i] = -1;		//质数显然k=1
    }
    for(int j = 1; j <= cnt && p[j]*i <= n; j++){
        mark[p[j]*i] = 1;
        if(i%p[j] == 0) break;
        mu[i*p[j]] = -mu[i];	//由于欧拉筛中,每个数只会被最小的质因子筛到一次,相当于这个数多了一个因子p[j],就相当于-mu[i](可以通过分类讨论理解)。
    }
}

同时,莫比乌斯函数还满足:
i = 1 n μ ( i ) = [ n = 1 ] \sum_{i=1}^{n}\mu(i)=[n=1]
这也是一个美妙的性质(可惜我不会证)。


莫比乌斯反演

依然直接扔公式(是的,我也不会证):

若函数 f ( n ) f(n) g ( n ) g(n) 是数论函数,且满足:
g ( n ) = d n f ( d ) g(n)=\sum_{d|n}f(d)
那么有如下结论:
f ( n ) = d n g ( d ) × μ ( n d ) f(n)=\sum_{d|n}g(d)\times \mu(\frac{n}{d})
实际上,在做题过程中也会遇到上述式子中的 d n d|n 变为 n d n|d ,式子依然成立,但此时 d d 往往有明确的上限。


经典套路

以下默认 n m n\leq m

1.求(i,j)=x的个数

这是莫比乌斯反演最经典的应用。

给定 n , m n,m ,求 f ( x ) = i = 1 n j = 1 m [ ( i , j ) = x ] f(x)=\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[(i,j)=x]​

我们设 g ( x ) = i = 1 n j = 1 m [ x ( i , j ) ] g(x)=\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[x|(i,j)] 。那么我们发现显然 g ( x ) = x d f ( d ) g(x)=\sum\limits_{x|d}f(d) 。此处 d d 的上限为 n n

于是我们套公式,直接反演一波:
f ( x ) = x d g ( d ) × μ ( d x ) f(x) = \sum_{x|d}g(d)\times \mu(\frac{d}{x})。
我们为啥这么麻烦的反演呢?当然是因为 g ( x ) g(x)​ 比较好求。我们观察 x ( i , j ) x|(i,j)​ 这个条件,发现 i , j i,j​ 均可以表示成 x × k x\times k​ ,于是在 n n​ m m​ 的范围内, g ( x ) = n x × m x g(x)=\lfloor \frac{n}{x} \rfloor \times \lfloor \frac{m}{x} \rfloor​

于是 f ( x ) = x d n d × m d × μ ( d x ) f(x)=\sum\limits_{x|d}\lfloor \frac{n}{d} \rfloor \times \lfloor \frac{m}{d} \rfloor \times \mu(\frac{d}{x})

扫描二维码关注公众号,回复: 5973911 查看本文章

接下来由于 x d x|d​ 不方便枚举,所以我们设 d = t × x d=t \times x​ ,则 f ( x ) = t = 1 n x n t x × m t x × μ ( t ) f(x)=\sum\limits_{t=1}^{\lfloor \frac{n}{x}\rfloor} \lfloor \frac{n}{tx} \rfloor \times \lfloor \frac{m}{tx} \rfloor \times \mu(t)​

提前处理使 n = n x n=\lfloor \frac{n}{x} \rfloor m = m x m=\lfloor \frac{m}{x} \rfloor f ( x ) = t = 1 n n t × m t × μ ( t ) f(x)=\sum\limits_{t=1}^{n}\lfloor \frac{n}{t} \rfloor \times \lfloor \frac{m}{t} \rfloor \times \mu(t)​

那么我们就可以愉快地 O ( n ) O(n)​ 求出所有的 f f​ 函数值了。

但对于多组数据, O ( n ) O(n)​ 复杂度显然是不够的,于是我们再用除法分块进行优化。由于此处有 n n​ m m​ 两个除法,所以在求除法分块的右边界时需要取最小值,保证两个除法结果均为连续相同的。同时我们对莫比乌斯函数做前缀和,一次分块的答案就是 ( s u m [ r ] s u m [ l 1 ] ) × n l × n l (sum[r]-sum[l-1])\times \lfloor\frac{n}{l}\rfloor \times \lfloor\frac{n}{l}\rfloor​ ,这样可以做到 O ( n ) O(\sqrt n)​ 回答。

例题:P4450 双亲数P3455 ZAP-Queries (都是板子题)。

小小的拓展(加个简单容斥):P2522 Problem b

代码:

void init(int n){
    mu[1] = 1;
    for(int i = 2; i <= n; i++){
        if(!mark[i]){
            p[++cnt] = i;
            mu[i] = -1;
        }
        for(int j = 1; j <= cnt && p[j]*i <= n; j++){
            mark[i*p[j]] = true;
            if(i%p[j] == 0) break;
            mu[i*p[j]] = -mu[i];
        }
    }
    for(int i = 2; i <= n; i++){
        mu[i] += mu[i-1];
    }
}

ll solve(ll n, ll m){
    if(n > m) swap(n, m);
    ll ans = 0;
    n /= k, m /= k;
    for(int l = 1, r; l <= n; l = r+1){
        r = min(n/(n/l), m/(m/l));
        ans += (mu[r]-mu[l-1])*(n/l)*(m/l);
    }
    return ans;
}


2.求(i,j)为质数的个数

给定 n , m n,m​ ,求 f ( x ) = i = 1 n j = 1 m [ ( i , j ) p r i m e ] f(x)=\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[(i,j) \in prime]​

题面与上面几乎一致,但是推式子难度更大,且需要结合上面的结论。

以下默认 p p r i m e p \in prime
f ( x ) = p i = 1 n j = 1 m [ ( i , j ) = p ] = p t = 1 n p n t p × m t p × μ ( t ) \begin {aligned} f(x) &amp;= \sum_{p}\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[(i,j)=p]\\ &amp;= \sum_{p}\sum\limits_{t=1}^{\lfloor \frac{n}{p}\rfloor} \lfloor \frac{n}{tp} \rfloor \times \lfloor \frac{m}{tp} \rfloor \times \mu(t) \\ \end{aligned}
看起来好像和上面一样化简完了,但时间复杂度却是 O ( c n t n ) O(cnt \sqrt n) ,其中 c n t cnt n n 以内的质数数量。

在这种情况下,就又出现了一种常见的优化套路。

我们设 T = t p T = tp ,结合 t t p p 的范围,显然 T [ 2 , n ] T\in[2,n]

于是我们不再枚举 t , p t,p ,而是直接枚举 T T
f ( x ) = T = 2 n p T n T × m T × μ ( T p ) = T = 2 n n T × m T × p T μ ( T p ) \begin{aligned} f(x)&amp;=\sum_{T=2}^{n}\sum_{p|T}\lfloor \frac{n}{T} \rfloor \times \lfloor \frac{m}{T} \rfloor \times \mu(\frac{T}{p})\\ &amp;=\sum_{T=2}^{n}\lfloor \frac{n}{T} \rfloor \times \lfloor \frac{m}{T} \rfloor \times \sum_{p|T}\mu(\frac{T}{p}) \end{aligned}
然后我们发现,前半部分就是我们熟悉的除法分块,而上一个式子复杂度高的原因正在后半部分需要枚举 p p 。考虑到这是多组询问,那么我们能不能把后半部分预处理出来呢?

很显然是可以的。

我们预处理时枚举 p p ,对于 p p 的倍数 T T ,都加上 μ ( T p ) \mu(\frac{T}{p}) 。复杂度是枚举质数再套枚举倍数,反正比原来小就对了。

然后对处理出来的答案做前缀和,再套除法分块即可。

预处理代码:

for(int i = 1; i <= cnt; i++){
    for(int j = 1; j*p[i] <= n; j++){
        s[j*p[i]] += mu[j];
    }
}
for(int i = 1; i <= n; i++){
    s[i] += s[i-1];
}

例题:P2257 YY的GCD


3.求约数个数和

d ( x ) d(x) 表示 x x 的约数和。给定 n , m n,m ,求 i = 1 n j = 1 m d ( i j ) \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}d(ij)

首先,有一个不知名的但是很NB的公式(没错,我又不会证,想证明可以看下面例题的题解):
d ( i j ) = x i y i [ g c d ( x , y ) = 1 ] d(ij)=\sum_{x|i}\sum_{y|i}[gcd(x,y)=1]
所以可以把原式进行一些转换:
a n s = i = 1 n j = 1 m x i y i [ g c d ( x , y ) = 1 ] \begin{aligned} ans &amp;= \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|i}[gcd(x,y)=1] \\ \end{aligned}
先枚举因数 x , y x,y
a n s = x = 1 n y = 1 m n x m y [ g c d ( x , y ) = 1 ] \begin{aligned} ans &amp;= \sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{y} \rfloor [gcd(x,y)=1] \end{aligned}

f ( k ) = x = 1 n y = 1 m n x m y [ g c d ( x , y ) = k ] g ( k ) = k d f ( d ) = x = 1 n y = 1 m n x m y [ k g c d ( x , y ) ] = x = 1 n k y = 1 m k n x k m y k \begin{aligned} 设f(k) &amp;= \sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{y} \rfloor [gcd(x,y)=k]\\ g(k)&amp;= \sum_{k|d}f(d) \\ &amp;= \sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{y} \rfloor [k|gcd(x,y)] \\ &amp;= \sum_{x=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{y=1}^{\lfloor \frac{m}{k} \rfloor}\lfloor \frac{n}{xk} \rfloor \lfloor \frac{m}{yk} \rfloor \end{aligned}

接下来套公式反演一波:
f ( k ) = k d g ( d ) × μ ( d k ) = k d μ ( d k ) × x = 1 n d y = 1 m d n x d m y d \begin{aligned} f(k)&amp;=\sum_{k|d}g(d)\times \mu(\frac{d}{k})\\ &amp;= \sum_{k|d}\mu(\frac{d}{k})\times \sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{y=1}^{\lfloor \frac{m}{d} \rfloor}\lfloor \frac{n}{xd} \rfloor \lfloor \frac{m}{yd} \rfloor \end{aligned}
那么我们发现我们的答案是 f ( 1 ) f(1) ,于是化简一下(顺便把原式中的 x , y x,y 换成 i , j i,j ):
a n s = f ( 1 ) = d = 1 n μ ( d ) × i = 1 n d j = 1 m d n i d m j d ans = f(1)=\sum_{d=1}^{n}\mu(d)\times \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}\lfloor \frac{n}{id} \rfloor \lfloor \frac{m}{jd} \rfloor
然后发现后面两项之间没有关系:
a n s = d = 1 n μ ( d ) × ( i = 1 n d n i d ) × ( j = 1 m d m j d ) ans =\sum_{d=1}^{n}\mu(d)\times (\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\lfloor \frac{n}{id} \rfloor)\times (\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} \lfloor \frac{m}{jd} \rfloor)
我们可以先用一次除法分块预处理 s ( x ) = i = 1 x x i s(x)=\sum\limits_{i=1}^{x} \lfloor\frac{x}{i} \rfloor ,然后对 n d \frac{n}{d} 再进行除法分块,同时对 μ \mu 求前缀和就可以 O ( n ) O(\sqrt n) 求出答案。

预处理 s s ,前缀和:

for(int i = 1; i <= n; i++) mu[i] += mu[i-1];
for(int i = 1; i <= n; i++){
    for(int l = 1, r; l <= i; l = r+1){
        r = i/(i/l);
        s[i] += (ll)(r-l+1)*(i/l);
    }
}

除法分块回答询问:

for(int l = 1, r; l <= n; l = r+1){
    r = min(n/(n/l), m/(m/l));
    ans += (mu[r]-mu[l-1])*s[n/l]*s[m/l];
}

例题:P3327 约数个数和


到这里,我遇到过的一些套路就已经讲完了,本文也暂时完结。但以后可能会有更多的套路,有可能再更新。(先挖坑再说,填不填全看心情)

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/89219146
今日推荐