莫比乌斯与欧拉函数及反演 / 杜教筛从入门到入土

\(\texttt{2019.10.11}\) 月考结束后开始填坑。学莫比乌斯反演需要深厚的小学数学基础 , 或许你还要知道什么是函数。

莫比乌斯反演没有用的 , 有函数性质就可以了。\(\text{Luogu}\) 博客 里有例题。

符号定义

符号 :

  • 艾佛森括号 :
    \([a] = \begin{cases} 1 & (a\ \text{is True}) \\ 0 & (a\ \text{is False}) \\ \end{cases}\)

  • \(k\{\}\) 代表整个序列每个数字。\(k_i\) 代表其中 \(k\{\}\) 其中一个数字。

  • \(x \perp y\) 代表 \(x\)\(y\) 互质。

函数 :

  • \(\text N(n)=n\)
  • \(\text I(n)=1\)
  • \(\epsilon(n)=[n=1]\)
  • 我们设 \(\text{Sum}(h,n)\)\(\sum\limits^{n}_{i=1} h(i)\)

积性函数

这种函数是一类函数的统称 , 假如 \(f\) 是此类函数且满足 (\(x \perp y\)) , 满足性质 :

\[f(x) \times f(y)=f(x \times y) \]

其中完全积性函数可以满足 \(x \not \perp y\) 的情况。

狄利克雷卷积

如果有积性函数 \(f,g\) , 那么它们的卷积就是 : (\(d\)\(n\) 的因数 , 且 \(*\) 为卷积而不是乘号)

\[(f*g)(n)=\sum\limits_{d|n} f(d) \times g(\frac{n}{d}) \]

满足封闭性 , 卷来卷去它还是积性函数 , 证明 : \((x \perp y)\)

\[\begin{aligned} & (f*g)(xy)\\ = & \sum\limits_{d|xy} f(d) \times g(\frac{xy}{d}) \\ = & \sum\limits_{a|x , b|y ,a\perp b} f(ab) \times g(\frac{xy}{ab}) \\ = & \sum\limits_{a|x , b|y} f(a) \times f(b) \times g(\frac{x}{a}) \times g(\frac{y}{b}) \\ = &\ (f*g)(x) \times (f*g)(y) \\ \end{aligned}\]

因为 \(x \perp y\) , 所以它们没有共同因数 , \(a \perp b\)

它满足交换律 (\(d\)\(\frac{n}{d}\) 出现相同) , 结合律证明 :

\[\begin{aligned} & ((f*g)(n)*h)(n) \\ = & ((\sum\limits_{d|n} f(d) \times g(\frac{n}{d}))*h)(n) \\ = & \sum\limits_{t|n} h(\frac{n}{t}) \times \sum\limits_{d|n} f(d) \times g(\frac{n}{d}) \\ = & \sum\limits_{d|n} h(\frac{n}{d})\times f(d) \times g(\frac{n}{d}) \\ = & \sum\limits_{d|n} (f*h)(n) \times g(\frac{n}{d}) \\ = & ((f*h)(n)*g)(n) \end{aligned}\]

它还满足分配率 , 证明 :

\[\begin{aligned} & ((f+g)*h)(n) \\ = & \sum\limits_{d|n} (f(d)+g(d)) \times h(\frac{n}{d}) \\ = & \sum\limits_{d|n} f(d)h(\frac{n}{d})+\sum\limits_{d|n} g(d)h(\frac{n}{d}) \\ = & (f*h)(n)+(g*h)(n) \end{aligned}\]

这个东西到后面会有用。


莫比乌斯函数

我们知道一个正整数 \(n\) 可以被分解为 : (分解质因数)

\[n=\sum\limits^{t}_{i=1} {p_i}^{k_i} \]

那么其莫比乌斯函数就是 :

\[\mu = \begin{cases} 1 & (n=1) \\ (-1)^t & (k\{\}<1) \\ 0 & (k_i>1) \\ \end{cases}\]

\(\mu\) 是一个积性函数 , 证明 :

\[xy=\sum\limits^{t_x+t_y}_{i=1} {p_i}^{k_i} \]

所以它们都是 \((-1)^{t_x+t_y}\)

\(\mu\) 有一个非常常用的性质 :

\[(\mu*\text I)(n)=\epsilon(n) \]

也就是所谓的 :

\[\sum\limits_{d|n} \mu(d)=[n=1] \]

证明一 : (你或许要知道一些组合数知识)

我们不妨把那个东西写成 :

\[(\mu*\text I)(n)=0 \ \ \ \ \ \ \ (n\not=1) \]

我们考虑只有这样子的数才可以有正或负的贡献 :

\[\sum\limits_{i=1}^t {p_i}^1 \]

那如果所到达的 \(t\) 是奇数 , 负数贡献 \(-1\)。如果是偶数 , 正数贡献 \(1\)。那么我们有很多在 \(n\) 中选取奇数或者偶数个素数组合成 \(n\) 的一个因数的做法 , 那么这个东西可以用 \(C^n_m\) 来表示。

我们考虑一个 \(n\)\(t_n\) , 那么它的贡献应该是 :

\[\begin{aligned} & \sum\limits^{\lfloor\frac{n}{2}\rfloor}_{i=1} C^n_{2i}-\sum\limits^{\lfloor\frac{n+1}{2}\rfloor}_{i=1} C^n_{2i-1} \end{aligned}\]

我们知道 \(C^n_m=C^n_{n-m}\) 。考虑一个 \(m\) 对应一个 \(n-m\) 。 考虑 \(n\) 是奇数 , \(m\) 是奇数 , 那么 \(n-m\) 肯定是一个偶数。如果 \(m\) 是偶数 , 那么 \(n-m\) 是一个奇数。

那么 \(n\) 不是奇数的时候 , \(C^n_m=C^{n-1}_m+C^{n-1}_{m-1}\) , 后面两个都是奇数为 \(0\) , 那么 \(n\) 为偶数的时候也是 \(0\)。得证。

证明二:

对于 \(n=1\),原式显然成立。

由于狄利克雷卷积的封闭性 , 可知 \(\mu*\text I\) 也是积性函数。
故对于 \(n > 1\),考虑将其分解质因数,于是只需考虑素数次幂的点值。

对于素数 \(p\) , \((\mu*\text I)(p^k) = \sum\limits_{i=0}^k \mu(p^i) = \mu(1) + \mu(p) = 0\)。故原式成立。

来自巨佬 \(\text{alpha1022}\)

有这个函数一般的题目都可以做 , 比莫比乌斯反演有用

例题

\(\texttt{Luogu}\) 博客。

莫比乌斯反演

假如有 \(f,g\) 两个积性函数 , 且满足 :

\[f(n)=(g*\text I)(n) \]

则有 :

\[g(n)=(\mu*f)(n) \]

证明 :

\[\begin{aligned} f(n) & = \sum\limits_{d|n} g(\frac{n}{d})\text I(d) \\ \sum\limits_{d|n}\mu(\frac{n}{d})f(d) & =\sum\limits_{d|n}\mu(\frac{n}{d})\text I(d)g(d) \\ \sum\limits_{d|n}\mu(\frac{n}{d})f(d) & =\sum\limits_{d|n}g(d)\epsilon(\frac{n}{d}) \\ \sum\limits_{d|n}\mu(\frac{n}{d})f(d) & =\sum\limits_{d|n}g(d) [d=n] \\ (f*\mu)(n) & = g(n)\\ \end{aligned}\]

简化一下就变成这样 :

\[\begin{aligned} f(n) & = (g* \text I)(n) \\ (f*\mu)(n) & = ((\text I*\mu)(n)*g)(n) \\ (f*\mu)(n) & = (\epsilon*g)(n) \\ (f*\mu)(n) & = g(n) \\ \end{aligned}\]

至于结合律的转化可以看上面证明。这个东西比较适合于证明。


欧拉函数

定义就很简单 , 逆元方面走这里 : \(\text{Link}\)

\[\phi(n)=\sum\limits^{n}_{i=1} [\gcd(i,n)=1] \]

欧拉反演

欧拉反演及 :

\[(\phi*\text I)(n)=\text N(n) \]

也就是所谓 :

\[\sum\limits_{d|n} \phi(d)=n \]

证明 :

\[\begin{aligned} n & = \sum\limits_{d|n} \sum\limits^{n}_{i=1} [\gcd(i,n)=d] \\ n & = \sum\limits_{d|n} \sum\limits^{\frac{n}{d}}_{i=1} [\gcd(i,\frac{n}{d})=1] \\ n & = \sum\limits_{d|n} \phi(\frac{n}{d}) \\ n & = (\phi*\text I)(n) \\ \end{aligned}\]

为什么 \(\sum\limits_{d|n} \sum\limits^{n}_{i=1} [\gcd(i,n)=d]\) 等于 \(n\) ?
假如你给 \(1 ... n\) 里面的数的贡献按照它与 \(n\)\(\gcd\) 分类 , 那么它们的贡献总和一定是 \(n\)。而既然是 \(n\)\(\gcd\) , 那么结果一定是 \(n\) 的因数。得证。


\(\phi\)\(\mu\) 的关系

这个东西很简单 :

\[\begin{aligned} \text N(n) = & (\phi* \text I)(n) \\ (\text N*\mu)(n)= & (\phi*(\mu * \text I))(n) \\ \sum\limits_{d|n} \frac{n \times \mu(d)}{d}= & \phi(n) \\ \end{aligned}\]


杜教筛

这个东西很神奇 , 可以在 \(O(n^{\frac{2}{3}})\) 的时间求 \(\sum\limits^n_{i=1} f(i)\) , 其中 \(f\) 为积性函数。

我们有一个 \(g\) 函数 , 如果是积性 , 那么我们有一个 \((f*g)(n)=h(n)\)。则有 :

\[\begin{aligned} \sum\limits^{n}_{i=1} h(i) & =\sum\limits^{n}_{i=1}\sum\limits_{d|i} f(d)g(\frac{i}{d}) \\ \text{Sum}(h,n) & =\sum\limits^{n}_{d=1} f(d) \sum\limits_{d|i} g(\frac{i}{d}) \\ \text{Sum}(h,n) & =\sum\limits^{n}_{d=1} f(d) \sum\limits^{\lfloor\frac{n}{d}\rfloor}_{i=1} g(i) \\ \text{Sum}(h,n) & =\sum\limits^{n}_{d=1} f(d)\times \text {Sum}(g,\lfloor\frac{n}{d}\rfloor) \\ \text{Sum}(h,n) & =f(1)\text{Sum}(g,n)+ \sum\limits^{n}_{d=2} f(d)\text {Sum}(g,\lfloor\frac{n}{d}\rfloor) \\ \text{Sum}(h,n)-\sum\limits^{n}_{d=2} f(d)\text {Sum}(g,\lfloor\frac{n}{d}\rfloor) & =f(1)\text{Sum}(g,n) \\ \text{Sum}(g,n) & =\frac{\text{Sum}(h,n)-\sum\limits^{n}_{d=2} f(d)\text {Sum}(g,\lfloor\frac{n}{d}\rfloor)}{f(1)} \\ \end{aligned}\]

此谓杜教筛的常用套路。假如我们可以在 \(O(1)\) 的时间内求出 \(\text{Sum}(h,n)\) , 并且 \(f\) 状态可定 ( 通常为 \(\text I\) 之类的 ), 就可以在不断递归最后面的 \(\text {Sum}(g,\lfloor\frac{n}{d}\rfloor)\) , 达到接近于 \(n^{\frac{2}{3}}\) 的复杂度。

复杂度证明 : \(\text{Ark}\)

求 : \(\sum \mu\)

如果 \(\text I(x)=1\),那我们知道 \((\mu * \text I)(n)=[n=1]\)。我们就把 \(g\) 代为 \(\text I\),把 \(h\) 代为 \([n=1]\)

\[\begin{aligned} \text{Sum}(f,n)= & \frac{\sum\limits^{n}_{i=1} h(i)-\sum\limits^{n}_{d=2} g(d)\ \text{Sum}(f,\frac{n}{d})}{g(1)} \\= & \frac{1-\sum\limits^{n}_{d=2} \text{Sum}(f,\frac{n}{d})}{1} \\ = & 1-\sum\limits^{n}_{d=2} \text{Sum}(f,\frac{n}{d})\end{aligned} \]

求 : \(\sum \phi\)

已知 \((\phi*\text I)(n)=n\)

\[\begin{aligned} \text{Sum}(f,n)= & \frac{\sum\limits^{n}_{i=1} h(i)-\sum\limits^{n}_{d=2} g(d)\ \text{Sum}(f,\frac{n}{d})}{g(1)} \\= & \frac{\sum\limits^{n}_{i=1} i- \sum\limits^{n}_{d=2} \text{Sum}(f,\frac{n}{d})}{1} \\= & \frac{i(i+1)}{2}- \sum\limits^{n}_{d=2} \text{Sum}(f,\frac{n}{d})\end{aligned} \]

\(\texttt{Code}\)

实现方法 : 预处理到一个比较大的数字 , 我是暴力 \(O(n \log n)\) 筛函数 , 所以调到 \(10^6\) 比较合适。其次我们每筛完一个值的答案就用链式前向星把它连起来 , 模拟无错哈希 , 分三个指针 \(from,reach,value\) 分别代表哈希后的值 , 原来的值 , 这个值所对的函数的前缀和答案。查找的时候就可以全部扫一遍看一看有没有求过。实测 \(tot \leq 10^4\) , 一个数字连最多会冲突 \(15\) 个 , 小常数。

Uses math;

Const
	total=100000; 
	prepare=1000000;
	hash_num=5000000;

var
	next,reach:array[-1..total] of longint;
	phi,mobius:array[-1..prepare] of longint;
	cnt:array[-1..hash_num] of longint;
	incea:array[-1..prepare] of int64;
    ans:array[-1..10,1..2] of qword;
	value:array[-1..total] of int64;
	i,j,tot,test,maxn:longint;

procedure Link(l,r:longint;sum:int64);
begin
	inc(tot); reach[tot]:=r; value[tot]:=sum;
	next[tot]:=cnt[l]; cnt[l]:=tot;
end;

function Sieve_phi(n:longint):qword;
var i,j,k:longint;
begin
	if n<=prepare then exit(incea[n]); k:=0;
	Sieve_phi:=0; j:=0; i:=cnt[n mod hash_num];
	while i<>-1 do begin inc(k); maxn:=max(maxn,k); if reach[i]=n then exit(value[i]); i:=next[i]; end; i:=2;
	repeat
		j:=n div (n div i);
		inc(Sieve_phi,Sieve_phi(n div i)*(j-i+1)); i:=j+1;
	until i>n;
	Sieve_phi:=n*(n+1) >> 1-Sieve_phi; Link(n mod hash_num,n,Sieve_phi);
end;

function Sieve_mobius(n:longint):int64;
var i,j,k:longint;
begin
	if n<=prepare then exit(incea[n]); k:=0;
	Sieve_mobius:=0; j:=0; i:=cnt[n mod hash_num];
	while i<>-1 do begin inc(k); maxn:=max(maxn,k); if reach[i]=n then exit(value[i]); i:=next[i]; end; i:=2;
	repeat
		j:=n div (n div i);
		inc(Sieve_mobius,Sieve_mobius(n div i)*(j-i+1)); i:=j+1;
	until i>n;
	Sieve_mobius:=1-Sieve_mobius; Link(n mod hash_num,n,Sieve_mobius);
end;

begin
	filldword(cnt,sizeof(cnt) >> 2,maxlongint << 1+1); 
	read(test); mobius[1]:=1; phi[1]:=1; for i:=1 to test do read(ans[i,1]);
	for i:=1 to prepare do phi[i]:=i;
	for i:=1 to prepare do for j:=2 to trunc(prepare/i) do dec(phi[i*j],phi[i]);
	for i:=1 to prepare do incea[i]:=incea[i-1]+phi[i]; 
	for i:=1 to test do ans[i,2]:=Sieve_phi(ans[i,1]);
	filldword(cnt,sizeof(cnt) >> 2,maxlongint << 1+1);
	fillchar(value,sizeof(value),0); fillchar(reach,sizeof(reach),0);
	fillchar(incea,sizeof(incea),0); fillchar(next,sizeof(next),0);
	for i:=1 to prepare do for j:=2 to trunc(prepare/i) do dec(mobius[i*j],mobius[i]);
	for i:=1 to prepare do incea[i]:=incea[i-1]+mobius[i]; 
	for i:=1 to test do begin writeln(ans[i,2],' ',Sieve_mobius(ans[i,1])); end;
end.

鸣谢 :

\(\text{DimensionTripper}\)\(\text{peng-ym}\)\(\text{alpha1022}\) 大佬

posted @ 2019-06-06 12:30  _ARFA  阅读(375)  评论(0编辑  收藏  举报