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.
  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/14408431.html
Copyright © 2011-2022 走看看