Mobius反演方法

<更新提示>

前置知识:建议有一点 D i r i c h l e t Dirichlet Dirichlet卷积的基础,会线性筛求积性函数。本文可能会持续更新。

可以看我以前在博客园的博客。


<正文>

0. 符号及约定

1. 1. 1. [ P ] [P] [P]是指正则表达式,当 P P P t r u e true true时, [ P ] = 1 [P]=1 [P]=1,当 P P P f a l s e false false时, [ P ] = 0 [P]=0 [P]=0

2. 2. 2. 约数个数函数定义为 τ ( n ) = ∑ d ∣ n 1 \tau(n)=\sum_{d|n}1 τ(n)=dn1

3. 3. 3. 约数和函数定义为 σ ( n ) = ∑ d ∣ n d \sigma(n)=\sum_{d|n}d σ(n)=dnd

4. 4. 4. 元函数定义为 e ( n ) = [ n = 1 ] e(n)=[n=1] e(n)=[n=1]

5. 5. 5. 恒等函数定义为 I ( n ) = 1 I(n)=1 I(n)=1

6. 6. 6. 单位函数定义为 ϵ k ( n ) = n k \epsilon_k(n)=n^k ϵk(n)=nk

7. 7. 7. 欧拉函数定义为 φ ( n ) = ∑ i = 1 n [ gcd ⁡ ( i , n ) = 1 ] \varphi(n)=\sum_{i=1}^n[\gcd(i,n)=1] φ(n)=i=1n[gcd(i,n)=1]

8. 8. 8. n = ∏ p i c i n=\prod p_i^{c_i} n=pici,则莫比乌斯函数定义为 μ ( n ) = { 1 n = 1 ( − 1 ) k ∀ c i = 1 0 ∃ c i > 1 \mu(n)=\begin{cases}1&n=1\\(-1)^k& \forall c_i=1\\0&\exist c_i>1\end{cases} μ(n)=1(1)k0n=1ci=1ci>1

9. 9. 9. 对于数论函数 f f f,若满足 gcd ⁡ ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1时,有 f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b),则称函数 f f f积性函数

10. 10. 10. 对于两个函数 f , g f,g f,g,定义他们的 d i r i c h l e t dirichlet dirichlet卷积为: ( f × g ) ( n ) = ∑ d ∣ n f ( d ) g ( n d ) (f\times g)(n)=\sum_{d|n}f(d)g(\frac{n}{d}) (f×g)(n)=dnf(d)g(dn)函数 f , g f,g f,g不必要是积性函数

1. 欧拉筛法

前置知识里已经提到过,以下给出本文可能会使用的线性筛代码:

int flag[N],Prime[N],cnt;
inline void EularSieve(void)
{
     
    for (int i=2;i<=Lim;i++)
    {
     
        if ( !flag[i] ) Prime[++cnt] = i;
        for (int j=1;j<=cnt&&i*Prime[j]<=Lim;j++)
        {
     
            flag[ i*Prime[j] ] = true;
            if ( i % Prime[j] == 0 ) break;
        }
    }
}

这是最朴素的欧拉筛法,不过当我们要筛一些比较复杂的积性函数的时候,线性筛方程的推导可能会非常复杂。

因此我们考虑引入一种更好的线性筛法,目的是能够方便的筛出积性函数的值。

首先,我们注意到积性函数的性质:当 gcd ⁡ ( a , b ) = 1 \gcd(a,b)=1 gcd(a,b)=1时,有 f ( a b ) = f ( a ) f ( b ) f(ab)=f(a)f(b) f(ab)=f(a)f(b)。那么对于一个任意的正整数 n n n,我们可以用算术基本定理进行分解:

n = ∏ i = 1 k p i c i n=\prod_{i=1}^kp_i^{c_i} n=i=1kpici

此时,任意的 i , j ∈ [ 1 , k ] i,j\in[1,k] i,j[1,k]都满足 gcd ⁡ ( p i c i , p j c j ) = 1 \gcd(p_i^{c_i},p_j^{c_j})=1 gcd(pici,pjcj)=1。那么我们就可以得到:

f ( n ) = f ( ∏ i = 1 k p i c i ) = ∏ i = 1 k f ( p i c i ) f(n)=f\left(\prod_{i=1}^kp_i^{c_i}\right)=\prod_{i=1}^kf(p_i^{c_i}) f(n)=f(i=1kpici)=i=1kf(pici)

那么我们就有一个想法:利用定义求出积性函数 f f f在所有素数幂出的取值,然后直接递推出所有函数值

具体地说,可以这样递推:

f[1] = 1;
for (int i=2;i<=Lim;i++)
    if ( i == Pow[i] ) f[i] = calc(p[i],e[i]);
    else f[i] = f[i/Pow[i]] * f[Pow[i]];

其中 p [ i ] p[i] p[i]代表数 i i i的最小素因子, e [ i ] e[i] e[i]代表 i i i的分解式中 p [ i ] p[i] p[i]这个素因子的指数, c a l c calc calc就是求素数幂处取值的函数,而 P o w [ i ] = p [ i ] e [ i ] Pow[i]=p[i]^{e[i]} Pow[i]=p[i]e[i],,这样是不是就符合我们的要求了呢?

那么现在我们的问题就是如何求出 p , e , P o w p,e,Pow p,e,Pow这三个数组,幸运的是,他们都可以在线性筛的过程中求。

根据欧拉筛法每次用一个数的最小素因子筛去这个合数,就可以更新这三个数组的值了。

代码如下:

int p[N],e[N],Pow[N],Prime[N],cnt;
inline void EularSieve(void)
{
     
    Pow[1] = p[1] = 1 , e[1] = 0;
    for (int i=2;i<=Lim;i++)
    {
     
        if ( p[i] == 0 )
            p[i] = Pow[i] = Prime[++cnt] = i , e[i] = 1;
        // 判定一个新素数
        for (int j=1;j<=cnt&&i*Prime[j]<=Lim;j++)
        {
     
            int Next = i * Prime[j];
            if ( p[i] == Prime[j] )
            {
     
                e[Next] = e[i] + 1;
                Pow[Next] = Pow[i] * Prime[j];
                // 有相同的素因子
                break;
            }
            else e[Next] = 1 , Pow[Next] = Prime[j];
            // 没有相同的素因子
        }
    }
    f[1] = 1;
    for (int i=2;i<=Lim;i++)
        if ( i == Pow[i] ) f[i] = calc(p[i],e[i]);
        else f[i] = f[i/Pow[i]] * f[Pow[i]];
}

2. D i r i c h l e t Dirichlet Dirichlet卷积

2.1 D i r i c h l e t Dirichlet Dirichlet卷积的性质

1. 1. 1. 两个积性函数 f f f g g g d i r i c h l e t dirichlet dirichlet卷积仍为积性函数。

证明:

设有两个积性函数 f f f g g g,则它们的 d i r i c h l e t dirichlet dirichlet卷积为:

h = f × g = ∑ d ∣ n f ( d ) g ( n d ) h=f\times g=\sum_{d|n}f(d)g(\frac{n}{d}) h=f×g=dnf(d)g(dn)

对于函数 h h h则可以得到:

h ( x ) h ( y ) = ( ∑ d 1 ∣ x f ( d 1 ) g ( x d 1 ) ) ( ∑ d 2 ∣ y f ( d 2 ) g ( y d 2 ) )   = ∑ d 1 ∣ x , d 2 ∣ y f ( d 1 d 2 ) g ( x y d 1 d 2 ) = ∑ d ∣ x y f ( d ) g ( x y d ) = h ( x y ) h(x)h(y)=\left (\sum_{d_1|x}f(d_1)g(\frac{x}{d_1})\right)\left(\sum_{d_2|y}f(d_2)g(\frac{y}{d_2})\right) \\ \ \\=\sum_{d_1|x,d_2|y}f(d_1d_2)g\left(\frac{xy}{d_1d_2}\right)\\=\sum_{d|xy}f(d)g\left(\frac{xy}{d}\right)=h(xy) h(x)h(y)=d1xf(d1)g(d1x)d2yf(d2)g(d2y) =d1x,d2yf(d1d2)g(d1d2xy)=dxyf(d)g(dxy)=h(xy)

故函数 h h h为积性函数。

2. 2. 2. d i r i c h l e t dirichlet dirichlet卷积满足交换律。

证明:

设有数论函数 f f f g g g,则有:

f × g = ∑ d ∣ n f ( n ) g ( n d ) = ∑ d ∣ n g ( n ) f ( n d ) = g × f f\times g=\sum_{d|n}f(n)g(\frac{n}{d})\\=\sum_{d|n}g(n)f(\frac{n}{d})=g\times f f×g=dnf(n)g(dn)=dng(n)f(dn)=g×f

3. 3. 3. d i r i c h l e t dirichlet dirichlet卷积满足结合律。

证明:

设有数论函数 f f f g g g h h h,则有:

( f × g ) × h = f × g × h = g × h × f = ( g × h ) × f = f × ( g × h ) (f\times g)\times h=f\times g \times h\\=g\times h \times f=(g\times h)\times f\\=f\times (g\times h) (f×g)×h=f×g×h=g×h×f=(g×h)×f=f×(g×h)

4. 4. 4. d i r i c h l e t dirichlet dirichlet卷积满足分配律。

证明:

设有数论函数 f f f g g g h h h,则有:

( g + h ) × f = ∑ d ∣ n ( g ( d ) + h ( d ) ) f ( n d ) = ∑ d ∣ n g ( d ) f ( n d ) + ∑ d ∣ n h ( d ) f ( n d ) = g × f + h × f (g+h)\times f=\sum_{d|n}(g(d)+h(d))f(\frac{n}{d}) \\=\sum_{d|n}g(d)f(\frac{n}{d})+\sum_{d|n}h(d)f(\frac{n}{d}) \\=g\times f+h\times f (g+h)×f=dn(g(d)+h(d))f(dn)=dng(d)f(dn)+dnh(d)f(dn)=g×f+h×f

2.2 常见的 D i r i c h l e t Dirichlet Dirichlet卷积

1. 1. 1. f × e = f f\times e=f f×e=f

2. 2. 2. ϵ = φ × I \epsilon=\varphi \times I ϵ=φ×I

3. 3. 3. τ = I × I \tau=I\times I τ=I×I

4. 4. 4. σ = ϵ × I \sigma=\epsilon\times I σ=ϵ×I

5. 5. 5. e = μ × I e=\mu\times I e=μ×I

6. 6. 6. φ = ϵ × μ \varphi=\epsilon\times \mu φ=ϵ×μ

7. 7. 7. σ = τ × φ \sigma=\tau \times \varphi σ=τ×φ

这些卷积的证明多数在『简单积性函数和dirichlet卷积』一文中有,此处篇幅受限,顾略去证明。

3. 下取整函数的性质

1. 1. 1. 对于 i ∈ [ x , ⌊ k ⌊ k x ⌋ ⌋ ] i\in \left [x, \left \lfloor \frac{k}{ \lfloor \frac{k}x{} \rfloor } \right \rfloor \right ] i[x,xkk] ⌊ k i ⌋ \lfloor \frac{k}{i} \rfloor ik的值都相等。

证明:

f ( x ) = ⌊ k ⌊ k x ⌋ ⌋ f(x)= \left \lfloor \frac{k}{ \lfloor \frac{k}{x} \rfloor } \right \rfloor f(x)=xkk,显然有 f ( x ) ≥ ⌊ k ( k x ) ⌋ = x f(x)\geq \left \lfloor \frac{k}{ ( \frac{k}{x} ) } \right \rfloor=x f(x)(xk)k=x,则可得 ⌊ k f ( x ) ⌋ ≤ ⌊ k x ⌋ \left \lfloor \frac{k}{f(x)} \right \rfloor\leq \left \lfloor \frac{k}{x} \right \rfloor f(x)kxk

从另一方面考虑,则有 ⌊ k f ( x ) ⌋ ≥ ⌊ k k ⌊ k / x ⌋ ⌋ = ⌊ k x ⌋ \left \lfloor \frac{k}{f(x)} \right \rfloor\geq\left \lfloor \frac{k}{ \frac{k}{\left \lfloor k/x \right \rfloor } } \right \rfloor=\lfloor \frac{k}{x} \rfloor f(x)kk/xkk=xk,则可得: ⌊ k f ( x ) ⌋ = ⌊ k x ⌋ \left \lfloor \frac{k}{f(x)} \right \rfloor=\left \lfloor \frac{k}{x} \right \rfloor f(x)k=xk

2. 2. 2. ⌊ n k ⌋ \left\lfloor\frac{n}{k}\right\rfloor kn最多只有 2 n 2\sqrt n 2n 种取值。

证明:

k ≤ n k\leq\sqrt n kn 时,至多只有 n \sqrt n n k k k,所以对应的取值只有 n \sqrt n n 种。

k > n k>\sqrt n k>n 时,有 1 ≤ ⌊ n k ⌋ ≤ n 1\leq\left\lfloor\frac{n}{k}\right\rfloor\leq\sqrt n 1knn ,所以对应的取值也只有 n \sqrt n n 种。

那么当我们要对某个有关下取整函数的值求和的时候,就可以使用如下的整除分块算法,时间复杂度 O ( n ) O(\sqrt n) O(n )

inline int calc(void)
{
     
    int res = 0;
    for (int l=1,r;l<=n;l=r+1)
    {
     
        r = n/l ? min( n/(n/l) , n ) : n;
        res += f(n/l);
    }
    return res;
}

对于二元的情况,其实也是一样的,我们考虑间断点合并,就可以证明其时间复杂度为 O ( n + m ) O(\sqrt n + \sqrt m) O(n +m )

inline int calclim(int n,int k,int l) {
      return k/l ? min( k/(k/l) , n ) : n; }
inline int calc(void)
{
     
    int res = 0;
    for (int l=1,r;l<=min(n,m);l=r+1)
    {
     
        r = calclim( min(n,m) , n , l );
        r = min( r , calclim( min(n,m) , m , r ) );
        // 此时,区间[l,r]中所有的 [n/l] 都相等,所有的 [m/l] 也都相等。 
        res += f( n/l , m/l );
    }
    return res;
}

3. 3. 3. m m m是正整数,则有 ⌊ ⌊ x ⌋ m ⌋ = ⌊ x m ⌋ \left\lfloor\frac{\lfloor x \rfloor}{m}\right\rfloor=\left\lfloor\frac{x}{m}\right\rfloor mx=mx

证明:

x = k m + r ( r < m ) x=km+r(rx=km+r(r<m),则 ⌊ ⌊ x ⌋ m ⌋ = ⌊ k + ⌊ r ⌋ m ⌋ = k   ⌊ x m ⌋ = ⌊ k + r m ⌋ = k \left\lfloor\frac{\lfloor x \rfloor}{m}\right\rfloor=\left\lfloor k+ \frac{\lfloor r \rfloor}{m}\right\rfloor=k\\ \ \\ \left\lfloor\frac{x}{m}\right\rfloor=\left\lfloor k+ \frac{r}{m}\right\rfloor=k mx=k+mr=k mx=k+mr=k

推论 ⌊ ⌊ k n ⌋ m ⌋ = ⌊ k n m ⌋ \left\lfloor\frac{\lfloor \frac{k}{n} \rfloor}{m}\right\rfloor=\left\lfloor\frac{k}{nm}\right\rfloor mnk=nmk

4. 反演原理

反演形式 1 1 1

∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ] ⟺ ∑ i = 1 n ∑ j = 1 m ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]\Longleftrightarrow\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\mu(d) i=1nj=1m[gcd(i,j)=1]i=1nj=1mdgcd(i,j)μ(d)

证明: 由 e = μ × I e=\mu \times I e=μ×I可得。

反演形式 2 2 2(因数形式莫比乌斯定理):

F ( n ) = ∑ d ∣ n f ( d ) ⟺ f ( n ) = ∑ d ∣ n μ ( d ) F ( n d ) F(n)=\sum_{d|n}f(d)\Longleftrightarrow f(n)=\sum_{d|n}\mu(d)F\left(\frac{n}{d}\right) F(n)=dnf(d)f(n)=dnμ(d)F(dn)

证明1: d i r i c h l e t dirichlet dirichlet卷积

已知 F = I × f F=I \times f F=I×f,则有 μ × F = μ × I × F \mu\times F=\mu\times I\times F μ×F=μ×I×F,故 μ × F = e × f = f \mu\times F=e\times f=f μ×F=e×f=f

证明2:代数反演

∑ d ∣ n μ ( d ) F ( n d ) = ∑ d ∣ n μ ( d ) ∑ d ′ ∣ n d f ( d ′ )   = ∑ d ′ ∣ n f ( d ′ ) ∑ d ∣ n d ′ μ ( d ) \sum_{d|n}\mu(d)F\left(\frac{n}{d}\right)=\sum_{d|n}\mu(d)\sum _{d'|\frac{n}{d}}f\left(d'\right)\\ \ \\ =\sum_{d'|n}f(d')\sum _{d|\frac{n}{d'}}\mu\left(d\right) dnμ(d)F(dn)=dnμ(d)ddnf(d) =dnf(d)ddnμ(d)

μ × I = e \mu\times I=e μ×I=e可知 ∑ d ∣ n d ′ μ ( d ) \sum_{d|\frac{n}{d'}}\mu(d) ddnμ(d) 0 0 0当且仅当 n d ′ = 1 \frac{n}{d'}=1 dn=1,所以 d ′ = n d'=n d=n,原式即为 f ( n ) f(n) f(n)

反演形式 3 3 3(倍数形式莫比乌斯定理):

F ( n ) = ∑ n ∣ d f ( d ) ⟺ f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) F(n)=\sum_{n|d}f(d)\Longleftrightarrow f(n)=\sum_{n|d}\mu\left(\frac{d}{n}\right)F(d) F(n)=ndf(d)f(n)=ndμ(nd)F(d)

证明:

好像没有找到 d i r i c h l e t dirichlet dirichlet卷积的证明,就先写一个代数证明吧。

d n = k \frac{d}{n}=k nd=k,则可以得到:

∑ n ∣ d μ ( d n ) F ( d ) = ∑ k = 1 ∞ μ ( k ) F ( n k )   = ∑ k = 1 ∞ μ ( k ) ∑ n k ∣ t f ( t ) = ∑ n ∣ t f ( t ) ∑ k ∣ t n μ ( k ) \sum_{n|d}\mu\left(\frac{d}{n}\right)F(d)=\sum_{k=1}^{\infty}\mu(k)F(nk)\\ \ \\ =\sum_{k=1}^{\infty}\mu(k)\sum_{nk|t}f(t)=\sum_{n|t}f(t)\sum_{k|\frac{t}{n}}\mu(k) ndμ(nd)F(d)=k=1μ(k)F(nk) =k=1μ(k)nktf(t)=ntf(t)kntμ(k)

μ × I = e \mu\times I=e μ×I=e可知 ∑ k ∣ t n μ ( k ) \sum_{k|\frac{t}{n}}\mu(k) kntμ(k) 0 0 0当且仅当 t n = 1 \frac{t}{n}=1 nt=1,所以 t = n t=n t=n,原式即为 f ( n ) f(n) f(n)

5. 例题

5.1 前言

好了,终于到了例题环节,这一部分对于真正学会莫比乌斯反演才是最重要的。这一节将会通过例题着重讲解反演原理的运用。对于反演原理一节,形式 1 , 3 1,3 1,3最为重要,即使其本质基本相同,也希望读者能够时刻牢记。

5.2 BZOJ2301 Problem b

T T T组询问,求

∑ i = a b ∑ j = c d [ gcd ⁡ ( i , j ) = k ] \sum_{i=a}^b\sum_{j=c}^d[\gcd(i,j)=k] i=abj=cd[gcd(i,j)=k]

a , b , c , d , T , k ≤ 50000 a,b,c,d,T,k\leq 50000 a,b,c,d,T,k50000

首先,我们不妨将这个式子看成一个数表,第 i i i行第 j j j列的格子符合要求 gcd ⁡ ( i , j ) = k \gcd(i,j)=k gcd(i,j)=k,就有 1 1 1的价值。于是原式求的就是数表的子矩阵 ( a , c ) − ( b , d ) (a,c)-(b,d) (a,c)(b,d)的价值和。

这样我们就可以用二维前缀和的思想容斥

矩阵的价值和。即求:

∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = k ] \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k] i=1nj=1m[gcd(i,j)=k]

想必这个函数没法直接求吧,我们可以考虑运用倍数形式莫比乌斯定理,设:

f ( k ) = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = k ] , F ( x ) = ∑ x ∣ k f ( k ) f(k)=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k],F(x)=\sum_{x|k}f(k) f(k)=i=1nj=1m[gcd(i,j)=k],F(x)=xkf(k)

思考一下 F ( x ) F(x) F(x)是什么?函数 f f f x x x所有倍数处的取值之和。也就是说一对 ( i , j ) (i,j) (i,j)的最大公约数只要是 x x x的倍数,就会对 F ( x ) F(x) F(x)有一个 1 1 1的贡献。

我们知道 x ∣ gcd ⁡ ( i , j ) x|\gcd(i,j) xgcd(i,j)等价于 i , j i,j i,j都是 x x x的倍数,既然 i ∈ [ 1 , n ] , j ∈ [ 1 , m ] i\in[1,n],j\in[1,m] i[1,n],j[1,m],那么函数 F F F的值就很好求了。

F ( x ) = ∑ x ∣ k f ( k ) = ⌊ n x ⌋ ⌊ m x ⌋ F(x)=\sum_{x|k}f(k)=\left\lfloor\frac{n}{x}\right\rfloor\left\lfloor\frac{m}{x}\right\rfloor F(x)=xkf(k)=xnxm

我们知道了函数 F F F的求法,就可以大胆地套用倍数形式莫比乌斯定理了:

f ( k ) = ∑ k ∣ x μ ( x k ) F ( x ) = ∑ k ∣ x μ ( x k ) ⌊ n x ⌋ ⌊ m x ⌋ f(k)=\sum_{k|x}\mu\left(\frac{x}{k}\right)F(x)=\sum_{k|x}\mu\left(\frac{x}{k}\right)\left\lfloor\frac{n}{x}\right\rfloor\left\lfloor\frac{m}{x}\right\rfloor f(k)=kxμ(kx)F(x)=kxμ(kx)xnxm

我们可以枚举 k k k的倍数 O ( n ) O(n) O(n)计算答案了,但似乎这样还会超时。

回想起整除分块的优化了吗?我们不妨设 t = x k t=\frac{x}{k} t=kx,那么就有:

f ( k ) = ∑ t = 1 m i n ( n , m ) k μ ( t ) ⌊ n t k ⌋ ⌊ m t k ⌋ = ∑ t = 1 m i n ( n , m ) k μ ( t ) ⌊ ⌊ n / k ⌋ t ⌋ ⌊ ⌊ m / k ⌋ t ⌋ f(k)=\sum_{t=1}^{\frac{min(n,m)}{k}}\mu(t)\left\lfloor\frac{n}{tk}\right\rfloor\left\lfloor\frac{m}{tk}\right\rfloor=\sum_{t=1}^{\frac{min(n,m)}{k}}\mu(t)\left\lfloor\frac{\lfloor n/k\rfloor}{t}\right\rfloor\left\lfloor\frac{\lfloor m/k \rfloor}{t}\right\rfloor f(k)=t=1kmin(n,m)μ(t)tkntkm=t=1kmin(n,m)μ(t)tn/ktm/k

注意,上面的推导还用了下取整函数的性质 3 3 3

好了,我们可以线性筛预处理莫比乌斯函数,并求出他的前缀和,然后运用整除分块算法, O ( n + m ) O(\sqrt n+\sqrt m) O(n +m )计算每一组询问的答案。

参考代码如下:

#include 
using namespace std;
const int N = 50020;
int a,b,c,d,k,sum[N];
int cnt,Prime[N],p[N],e[N],Pow[N],mu[N];
inline void EularSieve(void)
{
     
    p[1] = Pow[1] = 1 , e[1] = 0;
    for (int i=2;i<=N-20;i++)
    {
     
        if ( p[i] == 0 )
            p[i] = Pow[i] = Prime[++cnt] = i , e[i] = 1;
        for (int j=1;j<=cnt&&i*Prime[j]<=N-20;j++)
        {
     
            int Next = i * Prime[j];
            p[Next] = Prime[j];
            if ( p[i] == Prime[j] )
            {
     
                e[Next] = e[i] + 1;
                Pow[Next] = Pow[i] * Prime[j];
                break;
            }
            else e[Next] = 1 , Pow[Next] = Prime[j];
        }
    }
    mu[1] = 1;
    for (int i=2;i<=N-20;i++)
        if ( Pow[i] == i ) mu[i] = e[i] >= 2 ? 0 : -1;
        else mu[i] = mu[i/Pow[i]] * mu[Pow[i]];
    // 欧拉筛法+递推求莫比乌斯函数
}
inline void init(void)
{
     
    for (int i=1;i<=N-20;i++)
        sum[i] = sum[i-1] + mu[i];
    // 计算一下莫比乌斯函数的前缀和
}
inline int calclim(int n,int k,int l) {
      return k/l ? min( k/(k/l) , n ) : n; }
inline long long Mobius(int n,int m,int k)
{
     
    long long res = 0; n /= k , m /= k;
    for (int l=1,r;l<=min(n,m);l=r+1)
    {
     
        r = calclim( min(n,m) , n , l );
        r = min( r , calclim( min(n,m) , m , l ) );
        res += 1LL * ( sum[r] - sum[l-1] ) * (n/l) * (m/l);
        // 整除分块,直接计算答案
    }
    return res;
}
int main(void)
{
     
    EularSieve() , init();
    int T; scanf("%d",&T);
    while ( T --> 0 )
    {
     
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        long long ans = Mobius( b , d , k );
        ans -= Mobius( b , c-1 , k );
        ans -= Mobius( a-1 , d , k );
        ans += Mobius( a-1 , c-1 , k );
        // 容斥一下,求原问题的答案
        printf("%lld\n",ans);
    }
    return 0; // 拜拜程序
}

5.3 BZOJ2820 YY的GCD

T T T组询问,求

∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) ∈ P r i m e ] \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)\in Prime] i=1nj=1m[gcd(i,j)Prime]

n , m ≤ 1 0 7 , T ≤ 10000 n,m\leq 10^7,T\leq 10000 n,m107,T10000

有了上一题的经验,相信大家可以轻松推导到这一步:

a n s = ∑ p ∈ P r i m e ∑ t = 1 m i n ( n , m ) p μ ( t ) ⌊ n t p ⌋ ⌊ m t p ⌋ ans=\sum_{p\in Prime}\sum_{t=1}^{\frac{min(n,m)}{p}}\mu(t)\left\lfloor\frac{n}{tp}\right\rfloor\left\lfloor\frac{m}{tp}\right\rfloor ans=pPrimet=1pmin(n,m)μ(t)tpntpm

直接枚举质数 p p p求和,由素数定理可知时间复杂度为 O ( n ln ⁡ n ( n + m ) ) O(\frac{n}{\ln n}(\sqrt n+\sqrt m)) O(lnnn(n +m )),仍然会超时。

由于 p p p也是需要枚举的未知量,此时我们不妨设 t p = T tp=T tp=T,则原式可以化为:

∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ ⌊ m T ⌋ ∑ p ∈ P r i m e ∣ T μ ( T p ) \sum_{T=1}^{min(n,m)}\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{p\in Prime|T}\mu\left(\frac{T}{p}\right) T=1min(n,m)TnTmpPrimeTμ(pT)

再设 f ( T ) = ∑ p ∈ P r i m e ∣ T μ ( T p ) f(T)=\sum_{p\in Prime|T}\mu\left(\frac{T}{p}\right) f(T)=pPrimeTμ(pT),那么就有:

a n s = ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ ⌊ m T ⌋ f ( T ) ans=\sum_{T=1}^{min(n,m)}\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor f(T) ans=T=1min(n,m)TnTmf(T)

借鉴上一题的思路,如果我们能够预处理出函数 f f f的值,并求出前缀和,就也可以整除分块回答询问了。

不幸的是,函数 f f f和质数有关,不是积性函数,不能用我们之前的方法递推。

但是我们发现 n n n以内素数大约只有 n ln ⁡ n \frac{n}{\ln n} lnnn个,直接枚举每一个质数去更新它的倍数,均摊每个质数也只会更新 ln ⁡ n \ln n lnn次,所以暴力求值的时间复杂度也不会超时,大约是 O ( n ) O(n) O(n)的。

这个函数的递推其实有严格线性的方法,不过鉴于要推导更复杂的公式,在这道题中完全没有必要,故本文不再赘述。

参考代码:

#include 
using namespace std;
typedef long long ll;
const int N = 10000020;
int cnt,Prime[N],p[N],e[N],Pow[N],mu[N],f[N],a,b;
inline void EularSieve(void)
{
     
    p[1] = Pow[1] = 1 , e[1] = 0;
    for (int i=2;i<=N-20;i++)
    {
     
        if ( p[i] == 0 )
            p[i] = Pow[i] = Prime[++cnt] = i , e[i] = 1;
        for (int j=1;j<=cnt&&i*Prime[j]<=N-20;j++)
        {
     
            int Next = i * Prime[j];
            p[Next] = Prime[j];
            if ( p[i] == Prime[j] )
            {
     
                e[Next] = e[i] + 1;
                Pow[Next] = Pow[i] * Prime[j];
                break;
            }
            else e[Next] = 1 , Pow[Next] = Prime[j];
        }
    }
    mu[1] = 1;
    for (int i=2;i<=N-20;i++)
        if ( Pow[i] == i ) mu[i] = e[i] >= 2 ? 0 : -1;
        else mu[i] = mu[i/Pow[i]] * mu[Pow[i]];
    for (int i=1;i<=cnt;i++)
        for (int j=1;j*Prime[i]<=N-20;j++)
            f[j*Prime[i]] += mu[j];
    // 枚举质数暴力更新
    for (int i=1;i<=N-20;i++) f[i] += f[i-1];
}
inline int calclim(int n,int k,int l) {
      return k/l ? min( k/(k/l) , n ) : n; }
inline ll Mobius(void)
{
     
    ll res = 0;
    for (int l=1,r;l<=min(a,b);l=r+1)
    {
     
        r = calclim( min(a,b) , a , l );
        r = min( r , calclim( min(a,b) , b , l ) );
        res += 1LL * ( f[r] - f[l-1] ) * (a/l) * (b/l);
    }
    return res;
}
int main(void)
{
     
    EularSieve();
    int T; scanf("%d",&T);
    while ( T --> 0 )
        scanf("%d%d",&a,&b),
        printf("%lld\n",Mobius());
    return 0;
}


<后记>

你可能感兴趣的:(Mobius反演方法)