公式渲染测试。
前置知识
小碎骨
艾佛森括号
此处 是一个可真可假的命题。
引理1
证明
数论分块
内容独立了出来,详细内容见 数论分块 - Luckyblock
对于一类含有的求和式 (
为常数),由于
单调不增,故存在多个区间
, 使得
成立。
对于任意一个,最大的满足上式的
积性函数
定义
若 且
,则
为积性函数。
性质
若 ,
均为积性函数,则以下函数也为积性函数:
$$
常见积性函数
- 单位函数
。
- 幂函数
,
通常简记为
。
- 常数函数
。
- 因数个数
。
- 除数函数
。
时为因数和函数,通常简记为
。
时为因数个数函数
。
- 欧拉函数
。
- 莫比乌斯函数
不是很懂上面写的什么玩意?
不用深究,有个印象继续往下看就好。
莫比乌斯函数
定义
为莫比乌斯函数,定义为
$$
解释
令 ,其中
为质因子,
。
1. 时,
。
2. 时 ,
时,
。
当某质因子出现次数大于时,
。
时,
。
当每个质因子只出现一次时,即,
中元素唯一。
,此处
为质因子的种类数。
性质
莫比乌斯函数是积性函数,且具有以下性质
证明,设 。
- 根据莫比乌斯函数定义,则有:
。
- 若
的某因子
,有
,则它由
个 本质不同的质因子组成。
由于质因子总数为,满足上式的因子数为
。
- 对于原求和式,转为枚举
的值。
则
根据二项式定理,上式,
易知该式在,即
时为
,否则为
。
反演常用结论
一个反演常用结论:
$$
证明 1:
设 ,则右
左。
证明 2:
暴力展开:
线性筛求莫比乌斯函数
为积性函数,因此可以线性筛莫比乌斯函数。
int pnum, mu[kMaxn], p[kMaxn]; bool vis[kMaxn]; void Euler(int lim_) { vis[1] = true, mu[1] = 1ll; for (int i = 2; i <= lim_; ++ i) { if (! vis[i]) { p[++ pnum] = i; mu[i] = - 1; } for (int j = 1; j <= pnum && i * p[j] <= lim_; ++ j) { vis[i * p[j]] = true; if (i % p[j] == 0) { //勿忘平方因子 mu[i * p[j]] = 0; break; } mu[i * p[j]] = - mu[i]; } } }
狄利克雷(Dirichlet)卷积
建议阅读 算法学习笔记(35): 狄利克雷卷积 By: Pecco。
定义两个数论函数 的狄利克雷卷积为
$$
性质
1. 显然满***换律,结合律,分配律。
2.为狄利克雷卷积的单位元,有
。
3. 若为积性函数,则
为积性函数。
关于单位元 
有:
证明
- 对于
,当且仅当
,即
时为
,否则为
。
- 则当
时,
。
当时,
。
综上,,满足单位元的性质。
则 成立。
除数函数与幂函数
幂函数 。
除数函数 。
显然有:
当 时,
,
为因数个数函数,有:
欧拉函数与恒等函数
对于一式,当 时(
为质数),有:
的因子有
个,为
,故
。
又 为积性函数,则对于任意正整数
,有:
得证。
对于 2 式,在 1 式基础上两侧同时 即得。
左侧变为 。
莫比乌斯反演
反演是个啥?反演。
公式
设为两个数论函数。
如果有
$$
证明
方法一:运用卷积。
原问题为:已知 ,证明
。
易知如下转化:。
方法二:对原式进行数论变换。
1. 用 替换
。
$$
2. 变换求和顺序。
$$
3. 依据 ,仅当
时,
,否则为
。
此时,故上式等价于
。
举例
从 狄利克雷(Dirichlet)卷积 部分可以知道一些积性函数的关系。
尝试对它们进行反演:
关于一类求和式
一般套路
考虑构造出函数 ,满足下列形式:
则求和式变为:
考虑算术基本定理,发现若满足 ,则
且
成立。
考虑 在何时做出贡献,调整枚举顺序:
等价于
中
的倍数的个数,则上式等价于:
数论分块求解即可。
例 1
发现此题的 等价于
,则上式等价于:
例 2
感悟
卷点什么东西,把 卷出来。
不一定是特殊意义的函数。
例题
[HAOI2011] Problem b
组询问,每次给定参数
,求:
$1\le n,k,a,b,c,d\le 5\times 10^4
a\le b,c\le d$。
令 。
根据容斥原理,则原式等价于 。
变成了上述一类求和式的形式,考虑化简
。
易知原式等价于
$$
代入反演常用结论 ,上式化为
$d\mid \gcd(i,j)
d\mid i
d\mid j
d\mid \gcd(i,j)$。
易知 中
的倍数有
个(由引理 1 可知),原式变为
$$
预处理 后,显然可以数论分块求解,复杂度
。
代码
//知识点:莫比乌斯反演 /* //By:Luckyblock */ #include #include #include #define ll long long const int MARX = 6e4 + 10; //============================================================= int N, a, b, c, d, k; int cnt, Mobius[MARX], Prime[MARX], Sum[MARX]; bool vis[MARX]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void GetMobius() { Mobius[1] = 1; int MAX = MARX - 10; for(int i = 2; i <= MAX; i ++) { if(! vis[i]) Mobius[i] = - 1, Prime[++ cnt] = i; for(int j = 1; j <= cnt && i * Prime[j] <= MAX; j ++) { vis[i * Prime[j]] = true; if(i % Prime[j] == 0) break; Mobius[i * Prime[j]] = - Mobius[i]; } } for(int i = 1; i <= MAX; i ++) Sum[i] = Sum[i - 1] + Mobius[i]; } ll Calc(int x, int y) { ll ans = 0ll; int max = std :: min(x, y); for(int l = 1, r; l <= max; l = r + 1) r = std :: min(x / (x / l), y / (y / l)), ans += (1ll * x / (1ll * l * k)) * (1ll * y / (1ll * l * k)) * (Sum[r] - Sum[l - 1]); return ans; } ll Solve() { a = read(), b = read(), c = read(), d = read(), k = read(); return Calc(b, d) - Calc(b, c - 1) - Calc(a - 1, d) + Calc(a - 1, c - 1); } //============================================================= int main() { N = read(); GetMobius(); while(N --) printf("%lld\n", Solve()); return 0; }
[国家集训队]Crash的数字表格
给定
, 求:
$1\le n,m\le 10^7$。
易知原式等价于:
考虑枚举 ,设枚举量为
。
则 的充要条件是满足
且
,则原式等价于:
先枚举 ,则原式等价于:
这个 很烦人,把
中的
提出来,变为枚举
,
。
消去 ,
的限定条件,则原式等价于:
单独考虑后半部分,设 。
发现 的形式与 [HAOI2011] Problem b 中的式子类似,代入
套路一波:
前半部分 ,可以考虑筛出
后求前缀和。
后半部分是等差数列乘等差数列的形式,设 ,
可以通过下式
计算:
则对于 ,有:
数论分块求解即可。
再看回原式,原式等价于:
又是一个可以数论分块求解的形式。
线性筛预处理后 数论分块套数论分块,复杂度 ,瓶颈是线性筛。
一些注意的点
处理时会出现求平方的运算,需要特别注意取模问题,ll 都会爆,被坑惨了。
在预处理前缀和的这个地方:
sum[i] = (sum[i - 1] + 1ll * i * i % kMod * (mu[i] + kMod)) % kMod; //仅令 mu + kMod
注意先对平方取模,在乘上 ,否则就会爆掉。
以及可以仅令 。
以及这个地方:
int g(int n_, int m_) { return (1ll * n_ * (n_ + 1ll) / 2ll % kMod) * (1ll * m_ * (m_ + 1ll) / 2ll % kMod) % kMod; }
平方计算,注意随时取模。
代码
//莫比乌斯反演 /* By:Luckyblock */ #include #include #include #include #include #define ll long long const ll kMod = 20101009; const int kMaxn = 1e7 + 10; //============================================================= int pnum, p[kMaxn]; ll mu[kMaxn], sum[kMaxn]; bool vis[kMaxn]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Getmax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void Getmin(int &fir_, int sec_) { if (sec_ < fir_) fir_ = sec_; } void Euler(int lim_) { vis[1] = true, mu[1] = 1ll; for (int i = 2; i <= lim_; ++ i) { if (! vis[i]) { p[++ pnum] = i; mu[i] = - 1; } for (int j = 1; j <= pnum && i * p[j] <= lim_; ++ j) { vis[i * p[j]] = true; if (i % p[j] == 0) { //勿忘平方因子 mu[i * p[j]] = 0; break; } mu[i * p[j]] = - mu[i]; } } sum[1] = 1ll; for (int i = 1; i <= lim_; ++ i) { sum[i] = (sum[i - 1] + 1ll * i * i % kMod * (mu[i] + kMod)) % kMod; //仅令 mu + kMod } } int g(int n_, int m_) { return (1ll * n_ * (n_ + 1ll) / 2ll % kMod) * (1ll * m_ * (m_ + 1ll) / 2ll % kMod) % kMod; } int f(int n_, int m_) { int lim = std :: min(n_, m_), ret = 0; for (int l = 1, r; l <= lim; l = r + 1) { r = std :: min(n_ / (n_ / l), m_ / (m_ / l)); ret = (ret + 1ll * (sum[r] - sum[l - 1] + kMod) * g(n_ / l, m_ / l) % kMod) % kMod; } return ret; } int Sum(ll l, ll r) { return (1ll * (r - l + 1ll) * (l + r) / 2ll) % kMod; } //============================================================= int main() { int n = read(), m = read(); int lim = std :: min(n, m), ans = 0; Euler(lim); for (int l = 1, r; l <= lim; l = r + 1) { r = std :: min(n / (n / l), m / (m / l)); ans = (ans + 1ll * Sum(l, r) * f(n / l, m / l) % kMod) % kMod; } printf("%d", ans); return 0; } /* 7718820 8445880 */
SP5971 LCMSUM - LCM Sum
次询问,每次询问给定
,求:
$1<T\le 3\times 10^5
1\le n\le 10^6$。
法一:无脑暴力
先拆 ,原式等价于:
套路的枚举 ,调换枚举顺序,原式等价于:
把 中的
提出来,变为枚举
,消去整除的条件,原式等价于:
代入 ,原式等价于:
值得注意的是 的上界为
,
。
调换枚举顺序,先枚举 ,原式等价于:
套路地消去整除的条件,把 中的
提出来,原式等价于:
对于最后的一个求和项,设 ,显然可以
求解,原式等价于:
考虑枚举 ,显然
。
与
无关,可以直接考虑枚举
,原式等价于:
前半块是一个数论分块的形式,可以 求解。
考虑后半块,设 ,发现它是一个积性函数,可以线性筛筛出,具体地:
其中 为
的最小质因子。
此时已经可以线性筛 + 数论分块求解,复杂度 ,比较菜鸡,时限 500ms 过不了。
考虑筛出 后再用埃氏筛预处理
,输出时乘上
,复杂度变为
。
法二:
同样先拆 ,枚举
,调换枚举顺序,原式等价于:
把 中的
提出来,变为枚举
,消去整除的条件,原式等价于:
调整枚举对象,上式等价于:
考虑 的实际意义,表示
中与
互质的数的和。
当 时,与
互质的数总是成对存在,即若
成立,则
成立。
每对这样的数的和为 ,共有
对这样的数。
则原式等价于:
$$
可以直接预处理答案。
预处理时先线性筛出 ,再埃氏筛枚举
的倍数,令它们的答案加上
,最后输出时乘上
。
复杂度 。
法二代码
//知识点:莫比乌斯反演 /* By:Luckyblock */ #include #include #include #include #define ll long long const int kMaxn = 1e6; //============================================================= ll phi[kMaxn + 10], ans[kMaxn + 10]; int pnum, p[kMaxn + 10]; bool flag[kMaxn + 10]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void GetMax(int &fir, int sec) { if (sec > fir) fir = sec; } void GetMin(int &fir, int sec) { if (sec < fir) fir = sec; } void GetPrime() { phi[1] = 1, flag[1] = true; //注意初始化 for (int i = 2; i <= kMaxn; ++ i) { if (! flag[i]) { p[++ pnum] = i; phi[i] = i - 1ll; } for (int j = 1; j <= pnum && i * p[j] <= kMaxn; ++ j) { flag[i * p[j]] = true; if (i % p[j]) { phi[i * p[j]] = phi[i] * phi[p[j]]; } else { phi[i * p[j]] = phi[i] * p[j]; break; } } } for (int i = 1; i <= kMaxn; ++ i) { for (int j = 1; i * j <= kMaxn; ++ j) { ans[i * j] += (i == 1 ? 1 : 1ll * phi[i] * i / 2); } } } //============================================================= int main() { GetPrime(); int T = read(); while (T --) { int n = read(); printf("%lld\n", 1ll * ans[n] * n); } return 0; }
[SDOI2015]约数个数和
次询问,每次询问给定
。
定义 >为
的约数个数,求:
$1<T,n\le 5\times 10^4$。
一个结论:
证明
先考虑 ,
的情况,有:
对于等式左侧, 的约数个数为
。
对于等式右侧,在保证 成立的情况下,有贡献的数对
只能是下列三种形式:
,
有
种取值方法。
,
有
种取值方法。
。
则等式右侧贡献次数为 次,等于
的约数个数。
则当 ,
时等式成立。
又不同质因子间相互独立,上述情况可拓展到一般的情况。
对 进行化简,代入
,原式等价于:
调换枚举顺序,先枚举 ,原式等价于:
把各项中的 提出来,消去整除的条件,原式等价于:
将 代回原式,原式等价于:
调换枚举顺序,先枚举 ,原式等价于:
把 中的
提出来,变为枚举
,消去整除的条件,原式等价于:
考虑预处理 ,则原式等价于:
线性筛预处理 ,数论分块求解即可,复杂度
。
代码
//知识点:莫比乌斯反演 /* By:Luckyblock */ #include #include #include #include #include #define ll long long const int kMaxn = 5e4 + 10; //============================================================= int pnum, p[kMaxn]; ll mu[kMaxn], num[kMaxn], d[kMaxn]; //num 为最小质因子的次数 ll summu[kMaxn], sumd[kMaxn]; bool vis[kMaxn]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Getmax(int &fir_, int sec_) { if (sec_ > fir_) fir_ = sec_; } void Getmin(int &fir_, int sec_) { if (sec_ < fir_) fir_ = sec_; } void Euler(int lim_) { vis[1] = true; mu[1] = d[1] = 1ll; for (int i = 2; i <= lim_; ++ i) { if (! vis[i]) { p[++ pnum] = i; mu[i] = - 1; num[i] = 1; d[i] = 2; } for (int j = 1; j <= pnum && i * p[j] <= lim_; ++ j) { vis[i * p[j]] = true; if (i % p[j] == 0) { //勿忘平方因子 mu[i * p[j]] = 0; num[i * p[j]] = num[i] + 1; d[i * p[j]] = 1ll * d[i] / num[i * p[j]] * (num[i * p[j]] + 1ll); break; } mu[i * p[j]] = - mu[i]; num[i * p[j]] = 1; d[i * p[j]] = 2ll * d[i]; // } } for (int i = 1; i <= lim_; ++ i) { summu[i] = summu[i - 1] + mu[i]; sumd[i] = sumd[i - 1] + d[i]; } } //============================================================= int main() { Euler(kMaxn - 10); int T = read(); while (T --) { int n = read(), m = read(), lim = std :: min(n, m); ll ans = 0ll; for (int l = 1, r; l <= lim; l = r + 1) { r = std :: min(n / (n / l), m / (m / l)); ans += 1ll * (summu[r] - summu[l - 1]) * sumd[n / l] * sumd[m / l]; // } printf("%lld\n", ans); } return 0; } /* in 1 32 43 */ /* out 15420 */
P3768 简单的数学题
给定参数
、
,求:
$n\leq10^{10}
5\times10^8\leq p\leq1.1\times10^9
p\in \mathrm{primes}$。
时限 4s。
无脑套路暴力。
考虑先枚举 ,原式等价于:
提出 ,变为枚举
与
,消去整除的条件,原式等价于:
代入 ,原式等价于:
值得注意的是 的上界为
,
。
调换枚举顺序,先枚举 ,原式等价于:
和上面一样,提出 ,套路地消去整除的条件,原式等价于:
发现后面两个求和是等差数列乘等差数列的形式。
设 ,
可以通过下式
计算:
代入原式,原式等价于:
考虑枚举 ,显然
。
再考虑枚举 ,即可得到
,原式等价于:
对于后面这一坨,用 反演,则原式等价于:
后半块可以数论分块,考虑前半块。
发现前半段即为 ,又是前缀和形式,考虑杜教筛。
有:
考虑找到一个函数 ,构造函数
使其便于求值,有:
看到同时存在 和
,考虑把
消去。
令 ,有:
又 ,则有:
找到了合适的 ,套杜教筛的公式。
前一项是自然数的立方和,有 。证明详见:自然数前n项平方和、立方和公式及证明 - 百度文库。
后一项直接等差数列求和 + 数论分块求解即可。
P5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题
因为非常喜欢这首曲子所以很想写下这题。
但是先咕咕咕咕着。
写在最后
参考资料:
Oi-Wiki-莫比乌斯反演
算法学习笔记(35): 狄利克雷卷积 By: Pecco
题解 SP5971 【LCMSUM - LCM Sum】 - BJpers2 的博客
题解 SP5971 【LCMSUM - LCM Sum】 - Venus 的博客
题解 P3327 【[SDOI2015]约数个数和】 - suncongbo 的博客
把 Oi-Wiki 上的内容进行了复制 整理扩充。我是个没有感情的复读机(大雾)