前言
复习了一下莫比乌斯反演和杜教筛,于是就有了这个博客。
当然,本文不止莫比乌斯反演和杜教筛,可能还有一些其他的东西。
1:一些前置知识
首先是一些前置知识,方便接下来的学习。
1.1:引理 1
描述
对于 (forall a, b, c in mathbb{Z}),有:
证明
设 (frac{a}{b} = leftlfloor frac{a}{b} ight floor + r),其中 (0 leq r < 1),则:
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),请你求出:
其中 (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)。
证明
- 第一步,证明其合法性((i leq j leq n))。
- 第二步,证明其为最大值。
考虑任意一个满足 (leftlfloor frac{n}{i} ight floor = leftlfloor frac{n}{j} ight floor) 的 (j):
因为 (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),请你求出:
其中 (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)) 也均为积性函数:
- 将 (x) 分解质因数,若 (f(x)) 为积性函数,则 (f(x) = prodlimits_{i = 1}^m f(p_i^{c_i}))。
2.3:常见积性函数
- 单位函数(完全积性):
- 恒等函数(完全积性):
- 常数函数(完全积性):
- 约数函数(积性):
- 欧拉函数(积性):
- 莫比乌斯函数(积性):
2.4:Dirichlet 卷积的定义
定义两个数论函数 (f, g) 的 Dirichlet 卷积为:
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
描述
证明
不难看出关于 (n) 的函数 (f(n) = sumlimits_{d | n} mu(d)) 是个积性函数。
-
当 (n = 1) 时,显然有 (f(1) = 1)。
-
当 (n > 1) 时,将 (n) 质因数分解,得:
综上所述,(f(n) = sumlimits_{d | n} mu(d) = [n = 1]),QED。
2.6.2:式 2
描述
证明
不难看出关于 (n) 的函数 (f(n) = sumlimits_{d | n} varphi(d)) 是个积性函数。
- 当 (n = 1) 时,显然有 (f(1) = 1)。
- 当 (n > 1) 时,将 (n) 质因数分解,得:
综上所述,(f(n) = sumlimits_{d | n} varphi(d) = n),QED。
2.6.3:式 3
描述
证明
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) 时所有挺好序列的价值和。
请你求出:
答案对 (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) 也一定互质。在唯一分解角度下观察,显然有:
故序列 (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) 时:
- 当 (c > d) 时:
求出 (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) 满足:
则:
证明
已知条件等价于:
两边同卷 (mu) 可得:
QED。
4.1:找不到原题了
Description
给出一个正整数 (n),求有多少个长度为 (n),循环节长度也恰好为 (n) 的小写字符串。
数据范围:(1 leq n leq 10^9)。
Solution
设长度为 (n) 的小写字符串有 (f(n)) 个;
设长度为 (n),循环节长度也恰好为 (n) 的小写字符串有 (g(n)) 个。
显然有:
以及:
根据莫比乌斯反演,可得:
上式就比较好求了,大家懂得都懂。
4.2:炫酷反演魔术
一般有关于 ([gcd(i, j) = 1]) 或 (gcd(i, j)) 以及一些奇奇怪怪的式子可以利用:
等等的一堆结论来反演。
4.3:Luogu P2522「HAOI2011」Problem b
Description
给出五个正整数 (a, b, c, d, 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)) 为:
简单容斥可得,答案为:
考虑这样一个 ( ext{calc}(n, m)) 要怎么求,注意到 (gcd(i, j) = k) 与 (gcd(frac{i}{k}, frac{j}{k}) = 1) 是等价的,故原式化为:
利用 (epsilon = mu ast 1) 反演,得:
交换枚举顺序,考虑先枚举 (d)。要使得 (d mid gcd(i, j)),只需要让 (d mid i) 且 (d mid j) 即可,故答案化为:
上式就比较好处理了。线性筛筛出莫比乌斯函数,求一遍莫比乌斯函数的前缀和,然后直接上数论分块。
实现上,可以先令 (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),请你求出:
答案对 (20101009) 取模。
数据范围:(1 leq n, m leq 10^7)。
Solution
不难发现原式为:
令 (d = gcd(i, j)),考虑交换枚举顺序:
将 (i) 和 (j) 中的 (d) 提取出来,得:
记:
考虑化简 (F(n, m)):
记:
则:
可以线性筛一下,然后数论分块。当 (n, m) 同阶时,计算 (F(n, m)) 是 (mathcal{O}(sqrt{n})) 的。
回到本题,可得答案为:
然后这个式子也可以数论分块做,本质上是一个数论分块套数论分块。
线性筛的复杂度是 (Theta(n)) 的。
数论分块套数论分块的复杂度是 (mathcal{O}(n^{frac{3}{4}}))。
于是这题就这样做完了。
关于「数论分块套数论分块」复杂度的证明
原问题中最后计算答案的式子是一个二维数论分块套二维数论分块,看起来不太爽。
我们把它替换成一个一维数论分块套一维数论分块,大概是这样:
其中,计算 (F(n)) 的复杂度是 (mathcal{O}(sqrt{n})) 的。考虑证明上式的复杂度。
可以分类讨论一下:
- 当 (d leq sqrt{n}) 时,最劣的情况是每一个 (leftlfloor frac{n}{d} ight floor) 互不相同,故此部分对复杂度的贡献为:
- 当 (d > sqrt{n}) 时,注意到 (leftlfloor frac{n}{d} ight floor) 的值会均会落在区间 ([1, sqrt{n}]) 中,最劣的情况是 (leftlfloor frac{n}{d} ight floor) 的值将 ([1, sqrt{n}]) 里的所有数都取过一遍,故此部分对复杂度的贡献为:
简单积分近似一下:
根据微积分基本定理,设 (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}})。则:
故数论分块套数论分块的复杂度是 (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})。
注意到答案为:
考虑继续化简,令 (T = pd),则上式化为:
记 (H(n) = sumlimits_{p | n, p in mathbb{P}} mu(frac{n}{p})),则原式化为:
考虑预处理出 (H(n))。可以使用 " 倍数法 ",对于每个质数 (p),让其去贡献它的每一个倍数 (T),稍微卡卡常也可以过。更高明的做法是挖掘 (H(n)) 的一些性质,然后使用线性筛将 (H(n)) 筛出来,这里直接给出结论:
预处理出 (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}) 有一些什么限制:
- 需要满足三角形的 " 两边之和大于第三边 ",即:
设 (e = frac{1 + sqrt 5}{2}),根据上面的两条限制,不难得出 (q) 关于 (p) 的取值范围:
- 需要满足三角形的三边都是整数,即:
由于 (q^2 mid x),可设 (i = frac{x}{q^2}),则答案为:
记 (S(n, p) = sumlimits_{i = 1}^n [gcd(i, p) = 1]),考虑化简该函数:
回到问题中,答案化为:
使用 " 倍数法 ",计算出函数 (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),必定会满足:
故有:
此时我们得到了一个 (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)) 的复杂度为:
简单积分近似一下:
可以证明,先线性筛预处理出 (f(x)) 的前 (n^frac{2}{3}) 项,可以取得杜教筛的理论最优复杂度 (mathcal{O}(n^frac{2}{3}))。
对于一些较大的 (n),需要使用 map
来存下对应的值,方便以后使用时直接计算出结果。
5.1:Luogu P4213 Sum
Description
给出一个正整数 (n),请你求出:
数据范围:(1 leq n < 2^{31})。
莫比乌斯函数前缀和
注意到 (mu ast 1 = epsilon),则考虑使用常数函数 (1(n) = 1) 来构造递推式:
然后直接筛即可。
欧拉函数前缀和
注意到 (varphi ast 1 = ext{ID}),则考虑使用常数函数 (1(n) = 1) 来构造递推式:
然后直接筛即可,但其实更好的做法是莫比乌斯反演。
#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),请你求出:
答案对给定的质数 (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}) 反演,得:
记 (F(n) = sumlimits_{i = 1}^n i = frac{n cdot (n + 1)}{2}),则答案化为:
考虑使用数论分块处理上式,现在的问题是要求出函数 (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) 卷在一起:
于是可以得到 (H ast ext{ID}_2 = ext{ID}_3),我们发现 ( ext{ID}_2) 与 ( ext{ID}_3) 的前缀和都可以直接 (Theta(1)) 计算(实在记不清楚公式的话,直接上拉格朗日插值吧),于是我们就可以得到 (S(n)) 的递推式:
直接上数论分块即可。
附:
[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 筛 "。