zoukankan      html  css  js  c++  java
  • 【随笔浅谈】莫比乌斯反演与杜教筛

    前言

    复习了一下莫比乌斯反演和杜教筛,于是就有了这个博客。

    当然,本文不止莫比乌斯反演和杜教筛,可能还有一些其他的东西。

    部分内容参考:http://oi-wiki.com/math/mobius/。

    1:一些前置知识

    首先是一些前置知识,方便接下来的学习。

    1.1:引理 1

    描述

    对于 (forall a, b, c in mathbb{Z}),有:

    [leftlfloor frac{a}{bc} ight floor = leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} ight floor ]

    证明

    (frac{a}{b} = leftlfloor frac{a}{b} ight floor + r),其中 (0 leq r < 1),则:

    [leftlfloor frac{a}{bc} ight floor = leftlfloor frac{a}{b} ast frac{1}{c} ight floor = leftlfloor frac{1}{c}left( leftlfloor frac{a}{b} ight floor + r ight) ight floor = leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} + frac{r}{c} ight floor = leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} ight floor ]

    QED。

    1.2:引理 2

    描述

    给出一个正整数 (n),对于正整数 (d(1 leq d leq n))(leftlfloor frac{n}{d} ight floor) 的取值不超过 (2sqrt{n}) 种。

    证明

    可以分类讨论:

    • (d leq sqrt{n}),因为 (d) 的取值不超过 (sqrt{n}) 种,显然 (leftlfloor frac{n}{d} ight floor) 的取值不超过 (sqrt{n}) 种。
    • (d > sqrt{n}),则 (leftlfloor frac{n}{d} ight floor leq sqrt{n}),故 (leftlfloor frac{n}{d} ight floor) 的取值不超过 (sqrt{n}) 种。

    综上所述,(leftlfloor frac{n}{d} ight floor) 的取值不超过 (2sqrt{n}) 种,QED。

    1.3:数论分块

    问题引入

    给出一个正整数 (n),请你求出:

    [sumlimits_{i = 1}^n leftlfloor frac{n}{i} ight floor ]

    其中 (1 leq n leq 10^9)

    分析

    观察到函数 (f(x) = frac{n}{x}) 在区间 ((0, +infty)) 上单调递减。
    所以,对于函数 (g(x) = leftlfloor frac{n}{x} ight floor) 的任意一个函数值,令 (g(x)) 取到该值的所有自变量 (x) 组成一段连续的区间。

    根据「1.2:引理 2」,我们知道这样的区间个数也是 (mathcal{O}(sqrt{n})) 级别的。

    对于一段满足 (g(x)) 均相等的区间,我们可以直接得出该区间里的所有 (x) 对答案的贡献。那现在的关键在于如何得到这些区间。

    考虑任意一个 (i(1 leq i leq n)),我们现在要找到一个最大的 (j(i leq j leq n)),使得 (leftlfloor frac{n}{i} ight floor = leftlfloor frac{n}{j} ight floor)
    结论是:(j = leftlfloor frac{n}{ leftlfloor frac{n}{i} ight floor } ight floor)

    证明

    1. 第一步,证明其合法性((i leq j leq n))。

    [egin{aligned} leftlfloor frac{n}{i} ight floor leq frac{n}{i} & implies leftlfloor frac{n}{leftlfloor frac{n}{i} ight floor} ight floor geq leftlfloor frac{n}{frac{n}{i}} ight floor = i \ & implies i leq leftlfloor frac{n}{leftlfloor frac{n}{i} ight floor} ight floor = j end{aligned} ]

    1. 第二步,证明其为最大值。

    考虑任意一个满足 (leftlfloor frac{n}{i} ight floor = leftlfloor frac{n}{j} ight floor)(j)

    [egin{aligned} leftlfloor frac{n}{i} ight floor = leftlfloor frac{n}{j} ight floor & implies leftlfloor frac{n}{i} ight floor leq frac{n}{j} < leftlfloor frac{n}{i} ight floor + 1 \ & implies frac{1}{leftlfloor frac{n}{i} ight floor + 1} < frac{j}{n} leq frac{1}{leftlfloor frac{n}{i} ight floor} \ & implies frac{n}{leftlfloor frac{n}{i} ight floor + 1} < j leq frac{n}{leftlfloor frac{n}{i} ight floor} end{aligned} ]

    因为 (j in mathbb{Z}),故 (j_{max} = leftlfloor frac{n}{ leftlfloor frac{n}{i} ight floor } ight floor)

    QED。

    实现

    根据上述结论,我们就有了找出这 (mathcal{O}(sqrt{n})) 个区间的方法。

    直接套结论做即可,时间复杂度是 (mathcal{O(sqrt{n})}) 的。

    for (int x = 1, Nx; x <= n; x = Nx + 1) {
    	Nx = n / (n / x);
    	ans += 1ll * (Nx - x + 1) * (n / x); 
    }
    

    1.4:多维的数论分块

    问题引入

    给出两个正整数 (n, m),请你求出:

    [sumlimits_{i = 1}^{min(n, m)} leftlfloor frac{n}{i} ight floor leftlfloor frac{m}{i} ight floor ]

    其中 (1 leq n, m leq 10^9)

    分析

    式子的乘积项包含两个取整分式。

    考虑任意一个 (i),我们现在要找到一个最大的 (j),使得 (leftlfloor frac{n}{i} ight floor = leftlfloor frac{n}{j} ight floor)(leftlfloor frac{m}{i} ight floor = leftlfloor frac{m}{j} ight floor)
    易证得:(j = minleft{leftlfloor frac{n}{ leftlfloor frac{n}{i} ight floor } ight floor, leftlfloor frac{m}{ leftlfloor frac{m}{i} ight floor } ight floor ight}),时间复杂度 (mathcal{O(sqrt{n})})

    扩展到多维也是一样的道理。

    2:积性函数与 Dirichlet 卷积

    简单介绍了积性函数与 Dirichlet 卷积。

    2.1:积性函数的定义

    若数论函数 (f(n)) 满足:对于任意互质正整数 (x, y),都有 (f(xy) = f(x) ast f(y))。则 (f(n))积性函数
    若数论函数 (f(n)) 满足:对于任意正整数 (x, y),都有 (f(xy) = f(x) ast f(y))。则 (f(n))完全积性函数

    2.2:积性函数的简单性质

    • 对于任意积性函数 (f(x)),都有 (f(1) = 1)
    • 对于任意积性函数 (f(x))(g(x)) 均为积性函数,则以下的 (h(x)) 也均为积性函数:

    [egin{aligned}h(x) & = f(x^p) \h(x) & = f^p(x) \h(x) & = f(x) ast g(x) \h(x) & = sumlimits_{d | x} f(d) ast g(frac{x}{d})end{aligned} ]

    • (x) 分解质因数,若 (f(x)) 为积性函数,则 (f(x) = prodlimits_{i = 1}^m f(p_i^{c_i}))

    2.3:常见积性函数

    • 单位函数(完全积性):

    [epsilon(n) = [n = 1] ]

    • 恒等函数(完全积性):

    [ ext{ID}_k(n) = n^k ]

    • 常数函数(完全积性):

    [1(n) = 1 ]

    • 约数函数(积性):

    [sigma_k(n) = sumlimits_{d | n} d^k ]

    • 欧拉函数(积性):

    [varphi(n) = n prodlimits_{i = 1}^m (1 - frac{1}{p_i}) ]

    • 莫比乌斯函数(积性):

    [mu(n) = egin{cases} 0 & exists i in [1, m], c_i > 1 \ 1 & m equiv 0 pmod 2, forall iin[1, m], c_i = 1 \ -1 & m equiv 1 pmod 2, forall i in [1, m], c_i = 1 end{cases} ]

    2.4:Dirichlet 卷积的定义

    定义两个数论函数 (f, g) 的 Dirichlet 卷积为:

    [(f ast g)(n) = sumlimits_{d | n} f(d) ast g(frac{n}{d}) ]

    2.5:Dirichlet 卷积的性质

    • 交换律:(f ast g = g ast f)
    • 结合律:((f ast g) ast h = f ast (g ast h))
    • 分配律:(f ast (g + h) = f ast g + f ast h)
    • (f ast epsilon = f),因此 (epsilon) 也被叫做 Dirichlet 卷积单位元。

    2.6:常见 Dirichlet 卷积式

    2.6.1:式 1

    描述

    [epsilon = mu ast 1 iff epsilon(n) = sumlimits_{d | n} mu(d) ]

    证明

    不难看出关于 (n) 的函数 (f(n) = sumlimits_{d | n} mu(d)) 是个积性函数。

    • (n = 1) 时,显然有 (f(1) = 1)

    • (n > 1) 时,将 (n) 质因数分解,得:

    [egin{aligned}f(n) & = prodlimits_{i = 1}^m f(p_i^{c_i}) \& = prodlimits_{i = 1}^m left( sumlimits_{d | p_i^{c_i}} mu(d) ight) \& = prodlimits_{i = 1}^m left( sumlimits_{j = 0}^{c_i} mu(p_i^j) ight) \& = prodlimits_{i = 1}^m left( mu(1) + mu(p_i) ight) \& = 0end{aligned} ]

    综上所述,(f(n) = sumlimits_{d | n} mu(d) = [n = 1]),QED。

    2.6.2:式 2

    描述

    [ ext{ID} = varphi ast 1 iff sumlimits_{d | n} varphi(d) = n ]

    证明

    不难看出关于 (n) 的函数 (f(n) = sumlimits_{d | n} varphi(d)) 是个积性函数。

    • (n = 1) 时,显然有 (f(1) = 1)
    • (n > 1) 时,将 (n) 质因数分解,得:

    [egin{aligned}f(n) & = prodlimits_{i = 1}^m f(p_i^{c_i}) \& = prodlimits_{i = 1}^m left( sumlimits_{d | p_i^{c_i}} varphi(d) ight) \& = prodlimits_{i = 1}^m left( sumlimits_{j = 0}^{c_i} varphi(p_i^j) ight) \& = prodlimits_{i = 1}^m left( 1 + sumlimits_{j = 1}^{c_i} varphi(p_i^j) ight) \& = prodlimits_{i = 1}^m left( 1 + sumlimits_{j = 1}^{c_i} left( p_i^j - p_i^{j - 1} ight) ight) \& = prodlimits_{i = 1}^m p_i^{c_i} \& = nend{aligned} ]

    综上所述,(f(n) = sumlimits_{d | n} varphi(d) = n),QED。

    2.6.3:式 3

    描述

    [varphi = mu ast ext{ID} iff varphi(n) = sumlimits_{d | n} d ast mu(frac{n}{d}) ]

    证明

    [egin{aligned}varphi ast 1 = ext{ID} & implies varphi ast 1 ast mu = mu ast ext{ID} \& implies varphi ast epsilon = mu ast ext{ID} \& implies varphi = mu ast ext{ID}end{aligned} ]

    QED。

    3:线性筛

    线性筛,也叫 Euler 筛。

    可以在线性时间内筛出 (1 sim n) 里的所有质数。

    相信大家都会。

    3.1:线性筛筛积性函数

    线性筛可以在线性时间内筛出大部分积性函数的前 (n) 项的函数值。

    一般地,对于积性函数 (f(x)),若 (f(p^c)) 的值便于分析。
    则该函数的前 (n) 项的函数值可以被线性筛筛出来。

    我们要多维护一个数组 ( ext{low}_i) 表示:设 (i) 的最小质因子为 (p_1),其次数为 (c_1),则 ( ext{low}_i = p_1^{c_1})

    在线性筛的过程中,有特殊的两点:

    • 若当前遇到了一个质数 (p),则将 (f(p), f(p^2), f(p^3), cdots, f(p^c)) 都计算出来。
    • 在计算至少包含两个质因子的 (i) 对应的函数值时,由积性函数定义得 (f(i) = f(frac{i}{ ext{low}_i}) imes f( ext{low}_i))

    这样就可以在线性时间内筛出一个积性函数的前 (n) 项了。

    当然,对于一些简单的积性函数(例如 (varphi, mu))就有更简单的筛法,不一定需要维护 ( ext{low}_i)

    3.1.1:线性筛筛欧拉函数

    线性筛筛欧拉函数的依据是这条性质:

    • (p) 为质数,若 (p mid n)(p^2 mid n),则 (varphi(n) = varphi(frac{n}{p}) ast p)
    • (p) 为质数,若 (p mid n)(p^2 mid n),则 (varphi(n) = varphi(frac{n}{p}) ast (p - 1))
    const int N = 10001000;
    
    int n;
    int k, prime[N], fac[N];
    int phi[N];
    
    void GetPrimes(int N) {
    	phi[1] = 1;
    	for (int i = 2; i <= N; i ++) {
    		if (!fac[i]) fac[i] = i, prime[++ k] = i, phi[i] = i - 1;
    		for (int j = 1; j <= k; j ++) {
    			if (prime[j] > fac[i] || prime[j] > N / i) break;
    			fac[i * prime[j]] = prime[j];
    			phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
    		}
    	}
    } 
    

    3.1.2:线性筛筛莫比乌斯函数

    线性筛筛莫比乌斯函数的依据是这条性质:

    • (p) 为质数,若 (p mid n)(p^2 mid n),则 (mu(n) = 0)
    • (p) 为质数,若 (p mid n)(p^2 mid n),则 (mu(n) = -mu(frac{n}{p}))
    const int N = 10001000;
    
    int n;
    int k, prime[N], fac[N];
    int miu[N];
    
    void GetPrimes(int N) {
    	miu[1] = 1;
    	for (int i = 2; i <= N; i ++) {
    		if (!fac[i]) fac[i] = i, prime[++ k] = i, miu[i] = -1;
    		for (int j = 1; j <= k; j ++) {
    			if (prime[j] > fac[i] || prime[j] > N / i) break;
    			fac[i * prime[j]] = prime[j];
    			miu[i * prime[j]] = miu[i] * (i % prime[j] ? -1 : 0);
    		}
    	}
    }
    

    3.1.3:线性筛筛各种积性函数

    根据积性函数的特殊性质。
    利用 ( ext{low}_i) 数组。
    可以在线性时间内筛出大部分积性函数的前 (n) 项的函数值。

    请读者好好品味这段代码。

    const int N = 10001000;
    
    int n;
    int k, prime[N], low[N];
    int f[N];
    
    void GetPrimes(int N) {
    	f[1] = 1;
    
    	for (int i = 2; i <= N; i ++) {
    		if (!low[i]) {
    			prime[++ k] = i;
    
    			long long v = i; int c = 1;
    			while (v <= N) {
    				low[v] = v;
    				// 计算 f(p^c)
    				v *= i, c ++;
    			}
    		}
    
    		for (int j = 1; j <= k; j ++) {
    			if (prime[j] > N / i) break;
    
    			if (i % prime[j] == 0)
    				low[i * prime[j]] = low[i] * prime[j];
    			else
    				low[i * prime[j]] = prime[j];
    
    			f[i * prime[j]] = f[(i * prime[j]) / low[i * prime[j]]] * f[low[i * prime[j]]];
    
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    

    3.2:「校内某模拟赛」挺好序列

    Description

    定义一个长度为 (n) 的挺好序列 (a_1, a_2, cdots, a_n),满足:对于 (forall i in [1, n]),都有 (a_i | A)
    定义一个长度为 (n) 的挺好序列 (a_1, a_2, cdots, a_n) 的价值为 (gcd(a_1, a_2, cdots, a_n, B)),其中 (B) 为定值。

    给出三个正整数 (n, m, B)

    定义 (f(x))(A = x) 时所有挺好序列的价值和。

    请你求出:

    [sumlimits_{i = 1}^m f(i) ]

    答案对 (998244353) 取模。

    数据范围:(1 leq n leq 10^{18})(1 leq m leq 2 imes 10^7)(1 leq B leq 10^{18})
    时空限制:(3000 ext{ms} /512 ext{MiB})

    Solution

    结论是:(f(x)) 是积性函数。

    考虑任意互质正整数 (x, y)。设序列 (a_1, a_2, cdots, a_n)(A = x) 时的某个挺好序列;设序列 (b_1, b_2, cdots, b_n)(A = y) 时的某个挺好序列。考虑生成序列 (c_1, c_2, cdots, c_n),其中 (c_i = a_i ast b_i),显然该序列是 (A = xy) 时的某个挺好序列。那么,考虑一下序列 (c) 的价值。

    因为 (x, y) 互质,所以 (a_i, b_i) 也一定互质。在唯一分解角度下观察,显然有:

    [gcd(a_1, a_2, cdots, a_n, B) ast gcd(b_1, b_2, cdots, b_n, B) = gcd(c_1, c_2, cdots, c_n, B) ]

    故序列 (c) 的价值为序列 (a, b) 的价值之积。

    回到积性函数的讨论上。(f(x))(A = x) 时所有满足要求的挺好序列的价值和,(f(y))(A = y) 时所有满足要求的挺好序列的价值和。那么 (f(x) ast f(y)) 可以被表示成 ((* + cdots + *)(* + cdots + *)) 形式的式子。

    把乘积项强行展开。
    展开后的每一项都是 " 左边括号里的某个数 " 乘上 " 右边括号里的某个数 "。
    所以展开后的每一项都为 (A = xy) 时的某个挺好序列的价值。
    所以 (f(x) ast f(y)) 即为 (A = xy) 时所有挺好序列的价值和。

    所以 (f(xy) = f(x) ast f(y)),所以 (f(x)) 为积性函数。


    注意到 (1 leq m leq 2 imes 10^7),可以考虑线性筛。

    按照上文「线性筛筛积性函数」的内容。
    现在的难点在于:对于一个质数 (p),如何求出 (f(p^c))

    对于一个质数 (p),令 (d) 为满足 (p^x mid B) 的最大的 (x)

    可以简单分类讨论一下,不难得出:

    • (c leq d) 时:

    [f(p^c) = sumlimits_{i = 0}^c p^i cdot left[ (c -i + 1)^n - (c - i)^n ight] ]

    • (c > d) 时:

    [f(p^c) = p^d cdot (c - d + 1)^n + sumlimits_{i = 0}^{d - 1} p^i cdot left[ (c -i + 1)^n - (c - i)^n ight] ]

    求出 (f(p), f(p^2), cdots, f(p^c)) 后,灵活运用线性筛即可求出 (f(x)) 的前 (m) 项,那么 (sumlimits_{i = 1}^m f(i)) 就直接算即可。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int power(int a, long long b, int p) {
    	int ans = 1;
    	for (; b; b >>= 1) {
    		if (b & 1) ans = 1ll * ans * a % p;
    		a = 1ll * a * a % p;
    	}
    	return ans;
    }
    
    const int N = 20001000;
    const int mod = 998244353;
    
    long long n;
    int m;
    long long B;
    
    int val[N];
    
    int k, prime[N], low[N];
    int f[N];
    
    int find(int p) {
    	long long v = 1;
    	int c = 0;
    
    	while (B % (v * p) == 0) {
    		v *= p;
    		c ++;
    	}
    
    	return c;
    }
    
    void GetPrimes(int N) {
    	f[1] = 1;
    	for (int i = 2; i <= N; i ++) {
    		if (!low[i]) {
    			prime[++ k] = i;
    
    			int d = find(i);
    
    			long long v = i; int c = 1;
    			while (v <= N) {
    				low[v] = v;
    
    				if (c <= d) {
    					int g = 1;
    					for (int j = 0; j <= c; j ++, g = 1ll * g * i % mod)
    						f[v] = (f[v] + 1ll * g * 
    						((val[c - j + 1] - val[c - j]) % mod + mod) % mod) % mod;
    				} else {
    					int g = 1;
    					for (int j = 0; j < d; j ++, g = 1ll * g * i % mod)
    						f[v] = (f[v] + 1ll * g * 
    						((val[c - j + 1] - val[c - j]) % mod + mod) % mod) % mod;
    					f[v] = (f[v] + 1ll * g * val[c - d + 1]) % mod;
    				}
    
    				v *= i, c ++;
    			}
    		}
    
    		for (int j = 1; j <= k; j ++) {
    			if (prime[j] > N / i) break;
    
    			if (i % prime[j] == 0)
    				low[i * prime[j]] = low[i] * prime[j];
    			else
    				low[i * prime[j]] = prime[j];
    
    			f[i * prime[j]] = 1ll * f[(i * prime[j]) / low[i * prime[j]]] * 
    			f[low[i * prime[j]]] % mod;
    
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    
    int main() {
    	scanf("%lld%d%lld", &n, &m, &B);
    
    	for (int i = 1; i <= 100; i ++) val[i] = power(i, n, mod);
    	GetPrimes(m);
    
    	int ans = 0;
    	for (int i = 1; i <= m; i ++)
    		ans = (ans + f[i]) % mod;
    
    	printf("%d
    ", ans);
    
    	return 0;
    }
    

    4:莫比乌斯反演

    描述

    已知两个数论函数 (f, g) 满足:

    [f(n) = sumlimits_{d mid n}g(d) ]

    则:

    [g(n) = sumlimits_{d mid n} f(d) ast mu(frac{n}{d}) ]

    证明

    已知条件等价于:

    [f = g ast 1 ]

    两边同卷 (mu) 可得:

    [egin{aligned}g ast epsilon & = f ast mu \g & = f ast muend{aligned} ]

    QED。

    4.1:找不到原题了

    Description

    给出一个正整数 (n),求有多少个长度为 (n),循环节长度也恰好为 (n) 的小写字符串。

    数据范围:(1 leq n leq 10^9)

    Solution

    设长度为 (n) 的小写字符串有 (f(n)) 个;
    设长度为 (n),循环节长度也恰好为 (n) 的小写字符串有 (g(n)) 个。

    显然有:

    [f(n) = 26^n ]

    以及:

    [f(n) = sumlimits_{d mid n} g(n) ]

    根据莫比乌斯反演,可得:

    [g(n) = sumlimits_{d | n} 26^d ast mu(frac{n}{d}) ]

    上式就比较好求了,大家懂得都懂。

    4.2:炫酷反演魔术

    一般有关于 ([gcd(i, j) = 1])(gcd(i, j)) 以及一些奇奇怪怪的式子可以利用:

    [egin{aligned}mu ast 1 & = epsilon \varphi ast 1 & = ext{ID} \mu ast ext{ID} & = varphi\& cdotsend{aligned} ]

    等等的一堆结论来反演。

    4.3:Luogu P2522「HAOI2011」Problem b

    Description

    给出五个正整数 (a, b, c, d, k),请你求出:

    [sumlimits_{a leq i leq b} sumlimits_{c leq j leq d} [gcd(i, j) = k] ]

    (T) 组询问。

    数据范围:(1 leq T, k leq 5 imes 10^4)(1 leq a leq b leq 5 imes 10^4)(1 leq c leq d leq 5 imes 10^4)

    Solution

    ( ext{calc}(n, m)) 为:

    [sumlimits_{i = 1}^n sumlimits_{j = 1}^m [gcd(i, j) = k] ]

    简单容斥可得,答案为:

    [ ext{calc}(b, d) - ext{calc}(a - 1, d) - ext{calc}(b, c - 1) + ext{calc}(a - 1, c - 1) ]

    考虑这样一个 ( ext{calc}(n, m)) 要怎么求,注意到 (gcd(i, j) = k)(gcd(frac{i}{k}, frac{j}{k}) = 1) 是等价的,故原式化为:

    [sumlimits_{i = 1}^{leftlfloor frac{n}{k} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{k} ight floor} [gcd(i, j) = 1] \sumlimits_{i = 1}^{leftlfloor frac{n}{k} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{k} ight floor} epsilonleft(gcd(i, j) ight) ]

    利用 (epsilon = mu ast 1) 反演,得:

    [sumlimits_{i = 1}^{leftlfloor frac{n}{k} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{k} ight floor} sumlimits_{d mid gcd(i, j)} mu(d) ]

    交换枚举顺序,考虑先枚举 (d)。要使得 (d mid gcd(i, j)),只需要让 (d mid i)(d mid j) 即可,故答案化为:

    [sumlimits_{d = 1}^{minleft( leftlfloor frac{n}{k} ight floor, leftlfloor frac{m}{k} ight floor ight)} mu(d) cdot leftlfloor frac{n}{kd} ight floor cdot leftlfloor frac{m}{kd} ight floor ]

    上式就比较好处理了。线性筛筛出莫比乌斯函数,求一遍莫比乌斯函数的前缀和,然后直接上数论分块。

    实现上,可以先令 (n gets leftlfloor frac{n}{k} ight floor, m gets leftlfloor frac{m}{k} ight floor),这样就比较好做了。

    时间复杂度 (mathcal{O}(N + Tsqrt{N})),其中 (N) 表示输入数据上界。

    4.4:Luogu P1829「国家集训队」Crash 的数字表格

    Description

    给出两个正整数 (n, m),请你求出:

    [sumlimits_{i = 1}^n sumlimits_{j = 1}^m ext{lcm}(i, j) ]

    答案对 (20101009) 取模。

    数据范围:(1 leq n, m leq 10^7)

    Solution

    不难发现原式为:

    [sumlimits_{i = 1}^n sumlimits_{j = 1}^m frac{i imes j}{gcd(i, j)} ]

    (d = gcd(i, j)),考虑交换枚举顺序:

    [sumlimits_{d = 1}^{min(n, m)} frac{1}{d} cdot sumlimits_{d mid i}^n sumlimits_{d mid j}^m [gcd(i, j) = d] i imes j ]

    (i)(j) 中的 (d) 提取出来,得:

    [sumlimits_{d = 1}^{min(n, m)} d cdot sumlimits_{i = 1}^{leftlfloor frac{n}{d} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{d} ight floor} [gcd(i, j) = 1] i imes j ]

    记:

    [F(n, m) = sumlimits_{i = 1}^{n} sumlimits_{j = 1}^{m} [gcd(i, j) = 1] i imes j ]

    考虑化简 (F(n, m))

    [egin{aligned}F(n, m) & = sumlimits_{i = 1}^n sumlimits_{j = 1}^m i imes j imes sumlimits_{w mid gcd(i, j)} mu(w) \& = sumlimits_{w = 1}^{min(n, m)} mu(w) ast w^2 imes sumlimits_{i = 1}^{leftlfloor frac{n}{w} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{w} ight floor} i imes jend{aligned} ]

    记:

    [g(n, m) = sumlimits_{i = 1}^n sumlimits_{j = 1}^m i imes j = sumlimits_{i = 1}^n i imes sumlimits_{j = 1}^m j = frac{n cdot (n + 1)}{2} imes frac{m cdot (m + 1)}{2} ]

    则:

    [F(n, m) = sumlimits_{w = 1}^{min(n, m)} mu(w) ast w^2 imes g(leftlfloor frac{n}{w} ight floor, leftlfloor frac{m}{w} ight floor) ]

    可以线性筛一下,然后数论分块。当 (n, m) 同阶时,计算 (F(n, m))(mathcal{O}(sqrt{n})) 的。

    回到本题,可得答案为:

    [sumlimits_{d = 1}^{min(n, m)} d cdot F(leftlfloor frac{n}{d} ight floor, leftlfloor frac{m}{d} ight floor) ]

    然后这个式子也可以数论分块做,本质上是一个数论分块套数论分块。

    线性筛的复杂度是 (Theta(n)) 的。
    数论分块套数论分块的复杂度是 (mathcal{O}(n^{frac{3}{4}}))

    于是这题就这样做完了。

    关于「数论分块套数论分块」复杂度的证明

    原问题中最后计算答案的式子是一个二维数论分块套二维数论分块,看起来不太爽。
    我们把它替换成一个一维数论分块套一维数论分块,大概是这样:

    [sumlimits_{d = 1}^n d cdot F(leftlfloor frac{n}{d} ight floor) ]

    其中,计算 (F(n)) 的复杂度是 (mathcal{O}(sqrt{n})) 的。考虑证明上式的复杂度。

    可以分类讨论一下:

    • (d leq sqrt{n}) 时,最劣的情况是每一个 (leftlfloor frac{n}{d} ight floor) 互不相同,故此部分对复杂度的贡献为:

    [sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrtfrac{n}{i} ight) ]

    • (d > sqrt{n}) 时,注意到 (leftlfloor frac{n}{d} ight floor) 的值会均会落在区间 ([1, sqrt{n}]) 中,最劣的情况是 (leftlfloor frac{n}{d} ight floor) 的值将 ([1, sqrt{n}]) 里的所有数都取过一遍,故此部分对复杂度的贡献为:

    [sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrt{i} ight) ]

    简单积分近似一下:

    [sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrt{i} ight) + sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrtfrac{n}{i} ight) hickapprox mathcal{O} left( int_{0}^{n^frac{1}{2}} left[ x^frac{1}{2} + left( frac{n}{x} ight)^frac{1}{2} ight] ext{dx} ight) ]

    根据微积分基本定理,设 (H(x) = frac{2}{3} x^{frac{3}{2}} + n^{frac{1}{2}} cdot 2x^{frac{1}{2}}),有 (H'(x) = x^{frac{1}{2}} + left( frac{n}{x} ight)^{frac{1}{2}})。则:

    [int_{0}^{n^frac{1}{2}} left[ x^frac{1}{2} + left( frac{n}{x} ight)^frac{1}{2} ight] ext{dx} = H(n^{frac{1}{2}}) - H(0) = frac{8}{3} n^{frac{3}{4}} ]

    故数论分块套数论分块的复杂度是 (mathcal{O}(n^frac{3}{4})) 的。

    4.5:Luogu P2257 YY 的 GCD

    Description

    给出两个正整数 (n, m),请你求出满足 (1 leq i leq n)(1 leq j leq m) 的所有数对 ((i, j)) 中,满足 (gcd(i, j)) 为质数的数对有多少个。

    (Q) 组数据。

    数据范围:(1 leq Q leq 10^4)(1 leq n, m leq 10^8)

    Solution

    为了方便叙述,设质数集为 (mathbb{P})

    注意到答案为:

    [sumlimits_{p in mathbb{P}} sumlimits_{i = 1}^n sumlimits_{j = 1}^m [gcd(i, j) = p] \sumlimits_{p in mathbb{P}} sumlimits_{i = 1}^{leftlfloor frac{n}{p} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{p} ight floor} [gcd(i, j) = 1] \sumlimits_{p in mathbb{P}} sumlimits_{i = 1}^{leftlfloor frac{n}{p} ight floor} sumlimits_{j = 1}^{leftlfloor frac{m}{p} ight floor} sumlimits_{d mid gcd(i, j)} mu(d) \sumlimits_{p in mathbb{P}} sumlimits_{d = 1}^{min(leftlfloor frac{n}{p} ight floor, leftlfloor frac{m}{p} ight floor)} mu(d) cdot leftlfloor frac{n}{pd} ight floor cdot leftlfloor frac{m}{pd} ight floor ]

    考虑继续化简,令 (T = pd),则上式化为:

    [sumlimits_{p in mathbb{P}} sumlimits_{d = 1}^{min(leftlfloor frac{n}{p} ight floor, leftlfloor frac{m}{p} ight floor)} mu(d) cdot leftlfloor frac{n}{T} ight floor cdot leftlfloor frac{m}{T} ight floor \sumlimits_{T = 1}^{min(n, m)} leftlfloor frac{n}{T} ight floor cdot leftlfloor frac{m}{T} ight floor cdot sumlimits_{p | T, p in mathbb{P}} mu(frac{T}{p}) ]

    (H(n) = sumlimits_{p | n, p in mathbb{P}} mu(frac{n}{p})),则原式化为:

    [sumlimits_{T = 1}^{min(n, m)} H(T) cdot leftlfloor frac{n}{T} ight floor cdot leftlfloor frac{m}{T} ight floor ]

    考虑预处理出 (H(n))。可以使用 " 倍数法 ",对于每个质数 (p),让其去贡献它的每一个倍数 (T),稍微卡卡常也可以过。更高明的做法是挖掘 (H(n)) 的一些性质,然后使用线性筛将 (H(n)) 筛出来,这里直接给出结论:

    [H(n) = egin{cases}1 & n in mathbb{P} \-H(frac{n}{p}) + mu(frac{n}{p}) & p mid n, p^2 mid n \mu(frac{n}{p}) & p mid n, p^2 mid nend{cases} ]

    预处理出 (H(n)) 后再处理出它的前缀和。这样就可以配合数论分块求出答案的值了。

    时间复杂度 (mathcal{O}(n + Qsqrt{n}))

    4.6:LOJ #6627「XXOI 2019」等比数列三角形

    Description

    给出一个正整数 (n)。请你求出:有多少个合法的三角形,满足三边都是 (leq n) 的整数,且三边成等比数列。

    数据范围:(1 leq n leq 10^{12})

    Solution

    枚举公差,将公差表示成一个既约分数 (k = frac{p}{q} geq 1) 的形式,也就是说我们要保证 (gcd(p, q) = 1)

    对于任意的一个三角形,不妨设三边中的最短边为 (x),则三边为 (x, kx, k^2x)

    考虑公差 (k = frac{p}{q}) 有一些什么限制:

    • 需要满足三角形的 " 两边之和大于第三边 ",即:

    [x + kx > k^2x \1 leq k < frac{1 + sqrt{5}}{2} ]

    (e = frac{1 + sqrt 5}{2}),根据上面的两条限制,不难得出 (q) 关于 (p) 的取值范围:

    [leftlceil frac{p}{e} ight ceil leq q leq p ]

    • 需要满足三角形的三边都是整数,即:

    [frac{p^2}{q^2}x in mathbb{Z} \q^2 mid x ]

    由于 (q^2 mid x),可设 (i = frac{x}{q^2}),则答案为:

    [sumlimits_{p} sumlimits_{leftlceil frac{p}{e} ight ceil leq q leq p} [gcd(p, q) = 1] sumlimits_{i = 1} [p^2 cdot ileq n] \sumlimits_{p = 1}^{sqrt{n}} leftlfloor frac{n}{p^2} ight floor cdot sumlimits_{leftlceil frac{p}{e} ight ceil leq q leq p} [gcd(p, q) = 1] ]

    (S(n, p) = sumlimits_{i = 1}^n [gcd(i, p) = 1]),考虑化简该函数:

    [egin{aligned}S(n, p) & = sumlimits_{i = 1}^n sumlimits_{d mid gcd(i, p)} mu(d) \& = sumlimits_{d mid p} mu(d) cdot leftlfloor frac{n}{d} ight floorend{aligned} ]

    回到问题中,答案化为:

    [sumlimits_{p = 1}^{sqrt{n}} leftlfloor frac{n}{p^2} ight floor cdot left[ S(n, p) - S(leftlceil frac{p}{e} ight ceil - 1, p) ight] ]

    使用 " 倍数法 ",计算出函数 (g(p) = S(n, p) - S(leftlceil frac{p}{e} ight ceil - 1, p)),然后直接算即可。

    时间复杂度 (mathcal{O}(sqrt{n} log sqrt{n}))

    当然,此题还有复杂度更优的高妙算法!

    5:杜教筛

    有时候,我们需要快速地对一些数论函数求前缀和,甚至是需要在低于线性时间的复杂度内求解。这时可以利用杜教筛来求出这些前缀和。

    一般地,给出一个数论函数 (f(x)),你需要求出 (S(n) = sumlimits_{i = 1}^n f(i))

    注意到,对于任意一个数论函数 (g),必定会满足:

    [egin{aligned}sumlimits_{i = 1}^n (f ast g)(i) & = sumlimits_{i = 1}^n sumlimits_{d mid i} g(d) cdot f(frac{i}{d}) \& = sumlimits_{i = 1}^n sumlimits_{j = 1}^{leftlfloor frac{n}{i} ight floor} g(i) cdot f(j) \& = sumlimits_{i = 1}^n g(i) cdot sumlimits_{j = 1}^{leftlfloor frac{n}{i} ight floor} f(j) \& = sumlimits_{i = 1}^n g(i) cdot S(leftlfloor frac{n}{i} ight floor)end{aligned} ]

    故有:

    [g(1)S(n) = sumlimits_{i = 1}^n (f ast g)(i) - sumlimits_{i = 2}^n g(i) cdot S(leftlfloor frac{n}{i} ight floor) ]

    此时我们得到了一个 (S(n)) 关于 (S(leftlfloor frac{n}{i} ight floor)) 的递推式。若 (f ast g)(g) 的前缀和都较好处理的话,就可以考虑使用数论分块来处理上面的递推式,较短时间内求出 (S(n)),因此找到一个合适的数论函数 (g) 是关键。

    如果直接使用数论分块做上面的递推式,根据 (leftlfloor frac{n}{d} ight floor) 的取值进行分段。 不考虑递归的情况下,单纯去处理一个 (S(n)) 的复杂度是 (mathcal{O}(sqrt{n})) 的,递归下去的复杂度也都是低阶小量 (o(sqrt{n})),故计算 (S(n)) 的复杂度为:

    [O(sqrt{n}) + sumlimits_{i = 1}^sqrt{n} mathcal{O}(sqrt{i}) + sumlimits_{i = 1}^sqrt{n} mathcal{O}left( sqrt{frac{n}{i}} ight) ]

    简单积分近似一下:

    [sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrt{i} ight) + sumlimits_{i = 1}^{sqrt{n}} mathcal{O} left( sqrtfrac{n}{i} ight) hickapprox mathcal{O} left( int_{0}^{n^frac{1}{2}} left[ x^frac{1}{2} + left( frac{n}{x} ight)^frac{1}{2} ight] ext{dx} ight) = mathcal{O}(n^{frac{3}{4}}) ]

    可以证明,先线性筛预处理出 (f(x)) 的前 (n^frac{2}{3}) 项,可以取得杜教筛的理论最优复杂度 (mathcal{O}(n^frac{2}{3}))

    对于一些较大的 (n),需要使用 map 来存下对应的值,方便以后使用时直接计算出结果。

    5.1:Luogu P4213 Sum

    Description

    给出一个正整数 (n),请你求出:

    [ans_1 = sumlimits_{i = 1}^n varphi(i) \ans_2 = sumlimits_{i = 1}^n mu(i) ]

    数据范围:(1 leq n < 2^{31})

    莫比乌斯函数前缀和

    注意到 (mu ast 1 = epsilon),则考虑使用常数函数 (1(n) = 1) 来构造递推式:

    [S(n) = 1 - sumlimits_{i = 2}^n S(leftlfloor frac{n}{i} ight floor) ]

    然后直接筛即可。

    欧拉函数前缀和

    注意到 (varphi ast 1 = ext{ID}),则考虑使用常数函数 (1(n) = 1) 来构造递推式:

    [S(n) = frac{n(n + 1)}{2} - sumlimits_{i = 2}^n S(leftlfloor frac{n}{i} ight floor) ]

    然后直接筛即可,但其实更好的做法是莫比乌斯反演

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map> 
    
    using namespace std;
    
    const int N = 1000100;
    const int SIZE = 1000000;
    
    int T;
    
    int n;
    
    int m, prime[N];
    int fac[N], miu[N];
    
    int S[N];
    
    void GetPrimes(int N) {
    	miu[1] = 1;
    	for (int i = 2; i <= N; i ++) {
    		if (!fac[i]) fac[i] = i, prime[++ m] = i, miu[i] = -1;
    		for (int j = 1; j <= m; j ++) {
    			if (prime[j] > fac[i] || prime[j] > N / i) break;
    			fac[i * prime[j]] = prime[j];
    			miu[i * prime[j]] = miu[i] * (i % prime[j] ? -1 : 0);
    		}
    	}
    
    	for (int i = 1; i <= N; i ++)
    		S[i] = S[i - 1] + miu[i];
    }
    
    map<int, int> G;
    
    int calc(int n) {
    	if (n <= SIZE) return S[n];
    	if (G[n]) return G[n];
    
    	int ans = 1;
    	for (long long x = 2, Nx; x <= n; x = Nx + 1) {
    		Nx = n / (n / x);
    		ans -= (Nx - x + 1) * calc(n / x);
    	}
    
    	return G[n] = ans;
    }
    
    void work() {
    	scanf("%d", &n);
    
    	long long ans1 = 0;
    	int ans2 = calc(n);
    
    	for (long long x = 1, Nx; x <= n; x = Nx + 1) {
    		Nx = n / (n / x);
    		ans1 += 1ll * (calc(Nx) - calc(x - 1)) * (n / x) * (n / x);
    	}
    
    	ans1 = (ans1 + 1) / 2;
    
    	printf("%lld %d
    ", ans1, ans2);
    }
    
    int main() {
    	GetPrimes(SIZE);
    
    	scanf("%d", &T);
    
    	while (T --)    work();
    
    	return 0;
    }
    

    5.2:Luogu P3768 简单的数学题

    Description

    给出一个正整数 (n),请你求出:

    [sumlimits_{i = 1}^nsumlimits_{j = 1}^n i cdot j cdot gcd(i, j) ]

    答案对给定的质数 (p) 取模。

    数据范围:(1 leq n leq 10^{10})(5 imes 10^8 leq p leq 1.1 imes 10^9)

    Solution

    利用 (varphi ast 1 = ext{ID}) 反演,得:

    [sumlimits_{i = 1}^n sumlimits_{j = 1}^n i cdot j cdot sumlimits_{d mid gcd(i, j)} varphi(d) \sumlimits_{d = 1}^n varphi(d) cdot sumlimits_{d mid i} sumlimits_{d mid j} i cdot j \sumlimits_{d = 1}^n varphi(d) cdot d^2 cdot sumlimits_{i = 1}^{leftlfloor frac{n}{d} ight floor} sumlimits_{j = 1}^{leftlfloor frac{n}{d} ight floor} i cdot j ]

    (F(n) = sumlimits_{i = 1}^n i = frac{n cdot (n + 1)}{2}),则答案化为:

    [sumlimits_{d = 1}^n varphi(d) cdot d^2 cdot F^2(leftlfloor frac{n}{d} ight floor) ]

    考虑使用数论分块处理上式,现在的问题是要求出函数 (H(n) = varphi(n) cdot n^2) 的前缀和,考虑杜教筛。

    线性筛的部分相信大家都会,主要是讨论构造 (S(n) = sumlimits_{i = 1}^n H(i)) 关于 (S(leftlfloor frac{n}{i} ight floor)) 递推式的部分。

    注意到恒等函数 ( ext{ID}_2),考虑将 (H)( ext{ID}_2) 卷在一起:

    [egin{aligned}(H ast ext{ID}_2)(n) & = sumlimits_{d mid n} varphi(d) cdot d^2 cdot left(frac{n}{d} ight)^2 \& = sumlimits_{d mid n} varphi(d) cdot n^2 \& = n^2 cdot sumlimits_{d | n} varphi(d) \& = n^3end{aligned} ]

    于是可以得到 (H ast ext{ID}_2 = ext{ID}_3),我们发现 ( ext{ID}_2)( ext{ID}_3) 的前缀和都可以直接 (Theta(1)) 计算(实在记不清楚公式的话,直接上拉格朗日插值吧),于是我们就可以得到 (S(n)) 的递推式:

    [S(n) = left( frac{n cdot (n + 1)}{2} ight)^2 - sumlimits_{i = 2}^n i^2 cdot S(leftlfloor frac{n}{i} ight floor) ]

    直接上数论分块即可。

    附:

    [sumlimits_{i = 1}^n i = frac{n cdot (n + 1)}{2} \sumlimits_{i = 1}^n i^2 = frac{n cdot (n + 1) cdot(2n + 1)}{6} \sumlimits_{i = 1}^n i^3 = left( frac{n cdot (n + 1)}{2} ight)^2 ]

    结语

    希望对大家有帮助。

    以后可能会补一补 " 洲阁筛 " 与 " min_25 筛 "。

    keep the love forever.
  • 相关阅读:
    在不给spring管理的类中获取类
    poi操作excel
    闭包
    输入url的过程发生了什么?
    跨域
    函数节流-防抖函数
    预解析-案例
    移动端适配方案
    实现元素水平居中和垂直居中的几种方法
    css小知识点
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/14408431.html
Copyright © 2011-2022 走看看