https://blog.csdn.net/consciousman/article/details/77888386
https://blog.csdn.net/skywalkert/article/details/50500009(偏理论)
之前做过不少的数论题,关于莫比乌斯与积性函数的数论题挺多的。。。特地过来总结一下。。当作自己的一个回顾了-_-
先安利一下神犇tls的博客和神犇PoPoQQQ的pdf !
膜拜tls…
跪popoqqq…
还有IOI金牌神犇任之州的集训队论文,都是好文啊!
需要先知道线性筛这个东西。。
orz…
线性筛的思想是每个合数都只会被它最小的质因数筛去,通过线性筛,我们可以O(n)得到1到n内一些数论函数的值,比如说欧拉函数、莫比乌斯函数、因子个数等等。。。
本文的除法均为整除,除非特别指出。
[expresion]为bool表达式,值为0或1,当且仅当expresion为真时为1,否则为0。
(i,j)表示gcd(i,j)。
首先定义莫比乌斯函数
u(i)=⎧⎩⎨1,(−1)k,0,if n = 1if n=p1∗p2∗...∗pk 其它u(i)={1,if n = 1(−1)k,if n=p1∗p2∗...∗pk 0,其它
根据上面的定义,n大于1时且n是平方因子数时莫比乌斯函数值为0,否则从n的唯一分解定理中根据素数的个数取奇偶即可。
莫比乌斯函数的性质:
1) ∑d|nu(d)=⎧⎩⎨1,0,if n = 1其它∑d|nu(d)={1,if n = 10,其它
有了上述这个式子,我们就可以直接简单地以O(nlogn)筛出1到n内所有数的莫比乌斯函数值了~
至此我们已经有两种办法求1到n内所有数的欧拉函数值了。
//O(n)
bool vis[N];
int primes[N], miu[N];
int init(int n) {
int tot = 0;
miu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = -1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > n)break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
}
}
//O(nlogn)
void init(int n) {
miu[1] = 1;
int t = n >> 1;
for (int i = 1; i <= t; i++) if (miu[i]) {
for (int j = i << 1; j <= n; j += i) miu[j] -= miu[i];
}
}
2) ∑d|nu(d)d=φ(n)n∑d|nu(d)d=φ(n)n
莫比乌斯函数除了可以用在莫比乌斯反演中之外,还可以用来进行容斥。
举一个常见的例子,求取1到n这个区间内有多少个数与x互质,一般的做法是直接进行容斥,但是我们可以发现容斥的系数刚好是莫比乌斯函数,即ans = ∑d|xu(d)∗nd∑d|xu(d)∗nd,其实这两者从本质考虑是完全等价的。
莫比乌斯反演是一个这样的式子:
定义F(n)=∑d|nf(n)F(n)=∑d|nf(n),那么可以得到f(n)=∑d|nu(nd)F(d)f(n)=∑d|nu(nd)F(d)
莫比乌斯反演还有一种更常见的形式:F(n)=∑n|df(d)F(n)=∑n|df(d), 那么有f(n)=∑n|du(dn)F(d)f(n)=∑n|du(dn)F(d)。
一般应用的都是上述的第二种形式,证明可以通过归纳得出,也可以直接通过式子变换得出,还可以由狄利克雷卷积证明。
f(n)=∑d|nu(nd)F(d)=∑d|nu(nd)∑x|df(x)=∑d|nf(d)∑x|ndu(x)=f(n)f(n)=∑d|nu(nd)F(d)=∑d|nu(nd)∑x|df(x)=∑d|nf(d)∑x|ndu(x)=f(n)
上述的证明中给出了一种常见的和式变换技巧:交换求和顺序。通过交换求和顺序,我们往往可以将某些和式化简或者更容易求出该和式,后面公式的化简将多次用到。
莫比乌斯反演还有一个推广式,如下:
设f(x)f(x)为一个数论函数,如果对于任意正整数a、b满足(a,b)=1(a,b)=1,有f(ab)=f(a)∗f(b)f(ab)=f(a)∗f(b)的话,我们称f(n)f(n)为积性函数;如果对于任意正整数啊a、b有f(ab)=f(a)∗f(b)f(ab)=f(a)∗f(b)的话,我们称f(n)f(n)为完全积性函数。
常见的积性函数有:
因子个数函数d(n)d(n),因子和函数σ(n)σ(n),二元函数gcd(a,b)gcd(a,b),欧拉函数φ(n)φ(n),莫比乌斯函数u(n)u(n)。
完全积性函数有:
元函数e(n)=[n==1]e(n)=[n==1],恒等函数I(n)=1I(n)=1,单位函数id(n)=nid(n)=n。
我们来看看积性函数的应用:
如果f(x)f(x)为积性函数且n=∑ti=1peiin=∑i=1tpiei,那么可以得到f(n)=∏ti=1f(peii)f(n)=∏i=1tf(piei),如果f(x)为完全积性函数,那么我们还可以进一步得到f(n)=∏ti=1f(pi)eif(n)=∏i=1tf(pi)ei。
举个简单的例子:因子个数d(n)=∏ti=1d(peii)=∏ti=1(ei+1)d(n)=∏i=1td(piei)=∏i=1t(ei+1),因子和函数σ(n)=∏ti=1σ(peii)=∏ti=1piei+1−1pi−1σ(n)=∏i=1tσ(piei)=∏i=1tpiei+1−1pi−1。
积性函数还可以用来进行线性筛从而得到很多数论函数的值~
比如下面的欧拉函数:
可以知道当i%p==0i%p==0时,φ(i∗p)=φ(i)∗pφ(i∗p)=φ(i)∗p,而当i%p!=0i%p!=0时,有
φ(i∗p)=φ(i)∗φ(p)=φ(i)∗(p−1)φ(i∗p)=φ(i)∗φ(p)=φ(i)∗(p−1)
。
const int N = 1e6 + 5;
bool vis[N];
int phi[N], p[N], cnt = 0;
void seive() {
cnt = 0;
phi[1] = 1;
for (int i = 2; i < N; i++) {
if (!vis[i]) p[cnt++] = i, phi[i] = i - 1;
for (int j = 0; j < cnt; j++) {
int s = i * p[j];
if (s > N) break;
vis[s] = 1;
if (i % p[j] == 0) {
phi[s] = p[j] * phi[i];
break;
}
phi[s] = (p[j] - 1) * phi[i];
}
}
}
积性函数的和也是积性函数,积性函数前缀和是一个常见的问题,常常需要低于线性时间内解决。
狄利克雷卷积
狄利克雷卷积是解决积性函数前缀和问题的一个重要工具。
定义
对两个算术函数f, g,定义其Dirichlet卷积为新函数f * g,满足(f∗g)(n)=∑d|nf(d)g(nd)(f∗g)(n)=∑d|nf(d)g(nd)。
狄利克雷卷积满足以下定律:
若f,g均为积性函数,则f*g也是积性函数。
我们可以O(nlogn)预处理两个函数的Dirichlet卷积。
LL f[N], g[N], h[N];
void calc(int n) {
for (int i = 1; i * i <= n; i++) {
h[i * i] += f[i] * g[i];
for (int j = i + 1; i * j <= n; j++) h[i * j] += f[i] * g[j] + f[j] * g[i];
}
}
举例
很多数论函数都可以用狄利克雷卷积来表示:
①约数个数函数d(n)=(I∗I)(n)d(n)=(I∗I)(n);
②约数和函数d(n)=(I∗id)(n)d(n)=(I∗id)(n);
③∑d|nu(d)=[n==1]∑d|nu(d)=[n==1],得到I∗u=eI∗u=e。
④∑d|nφ(d)=n∑d|nφ(d)=n,得到I∗φ=idI∗φ=id。
现在我们可以通过狄利克雷卷积来证明莫比乌斯反演了:
如果F(n)=∑d|nf(n)F(n)=∑d|nf(n),那么F = I*f,那么可以知道F*u = I*f*u=f*(I*u)=f*e=f,所以f(n)=∑d|nu(nd)F(d)f(n)=∑d|nu(nd)F(d)。
应用
设f(n)为一个数论函数,需要计算S(n)=∑ni=1f(i)S(n)=∑i=1nf(i),通常n>=1e9。
要解决上述问题,我们可以构造一个S(n)S(n)关于S(ni)S(ni)的递推式。
找到一个合适的数论函数g(n),
∑i=1n∑d|if(d)g(id)=∑i=1ng(i)∑j=1nif(j)=∑i=1ng(i)S(ni)∑i=1n∑d|if(d)g(id)=∑i=1ng(i)∑j=1nif(j)=∑i=1ng(i)S(ni)
这样,可以知道g(1)S(n)=∑ni=1(f∗g)(i)−∑ni=2g(i)∗S(ni)g(1)S(n)=∑i=1n(f∗g)(i)−∑i=2ng(i)∗S(ni)。
构造的卷积函数必须容易求得前缀和,这样我们可以记忆化地计算S(n),因为nini是分段的,可以计算出复杂度为O(n34)O(n34),只要我们预处理S函数前2323的值,复杂度就可以达到O(n23)O(n23)
上述的办法就是传说中的杜教筛了~
如果找不到适合的函数g(n),那么可以采用一种复杂度为O(n34logn)O(n34logn)的办法来实现求和,详见任之州的论文。
下面给出
之前做过不少的数论题,关于莫比乌斯与积性函数的数论题挺多的。。。特地过来总结一下。。当作自己的一个回顾了-_-
先安利一下神犇tls的博客和神犇PoPoQQQ的pdf !
膜拜tls…
跪popoqqq…
还有IOI金牌神犇任之州的集训队论文,都是好文啊!
需要先知道线性筛这个东西。。
orz…
线性筛的思想是每个合数都只会被它最小的质因数筛去,通过线性筛,我们可以O(n)得到1到n内一些数论函数的值,比如说欧拉函数、莫比乌斯函数、因子个数等等。。。
本文的除法均为整除,除非特别指出。
[expresion]为bool表达式,值为0或1,当且仅当expresion为真时为1,否则为0。
(i,j)表示gcd(i,j)。
首先定义莫比乌斯函数
u(i)=⎧⎩⎨1,(−1)k,0,if n = 1if n=p1∗p2∗...∗pk 其它u(i)={1,if n = 1(−1)k,if n=p1∗p2∗...∗pk 0,其它
根据上面的定义,n大于1时且n是平方因子数时莫比乌斯函数值为0,否则从n的唯一分解定理中根据素数的个数取奇偶即可。
莫比乌斯函数的性质:
1) ∑d|nu(d)=⎧⎩⎨1,0,if n = 1其它∑d|nu(d)={1,if n = 10,其它
有了上述这个式子,我们就可以直接简单地以O(nlogn)筛出1到n内所有数的莫比乌斯函数值了~
至此我们已经有两种办法求1到n内所有数的欧拉函数值了。
//O(n)
bool vis[N];
int primes[N], miu[N];
int init(int n) {
int tot = 0;
miu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[tot++] = i;
miu[i] = -1;
}
for (int j = 0; j < tot; j++) {
int k = i * primes[j];
if (k > n)break;
vis[k] = true;
if (i % primes[j]) miu[k] = -miu[i];
else break;
}
}
}
//O(nlogn)
void init(int n) {
miu[1] = 1;
int t = n >> 1;
for (int i = 1; i <= t; i++) if (miu[i]) {
for (int j = i << 1; j <= n; j += i) miu[j] -= miu[i];
}
}
2) ∑d|nu(d)d=φ(n)n∑d|nu(d)d=φ(n)n
莫比乌斯函数除了可以用在莫比乌斯反演中之外,还可以用来进行容斥。
举一个常见的例子,求取1到n这个区间内有多少个数与x互质,一般的做法是直接进行容斥,但是我们可以发现容斥的系数刚好是莫比乌斯函数,即ans = ∑d|xu(d)∗nd∑d|xu(d)∗nd,其实这两者从本质考虑是完全等价的。
莫比乌斯反演是一个这样的式子:
定义F(n)=∑d|nf(n)F(n)=∑d|nf(n),那么可以得到f(n)=∑d|nu(nd)F(d)f(n)=∑d|nu(nd)F(d)
莫比乌斯反演还有一种更常见的形式:F(n)=∑n|df(d)F(n)=∑n|df(d), 那么有f(n)=∑n|du(dn)F(d)f(n)=∑n|du(dn)F(d)。
一般应用的都是上述的第二种形式,证明可以通过归纳得出,也可以直接通过式子变换得出,还可以由狄利克雷卷积证明。
f(n)=∑d|nu(nd)F(d)=∑d|nu(nd)∑x|df(x)=∑d|nf(d)∑x|ndu(x)=f(n)f(n)=∑d|nu(nd)F(d)=∑d|nu(nd)∑x|df(x)=∑d|nf(d)∑x|ndu(x)=f(n)
上述的证明中给出了一种常见的和式变换技巧:交换求和顺序。通过交换求和顺序,我们往往可以将某些和式化简或者更容易求出该和式,后面公式的化简将多次用到。
莫比乌斯反演还有一个推广式,如下:
设f(x)f(x)为一个数论函数,如果对于任意正整数a、b满足(a,b)=1(a,b)=1,有f(ab)=f(a)∗f(b)f(ab)=f(a)∗f(b)的话,我们称f(n)f(n)为积性函数;如果对于任意正整数啊a、b有f(ab)=f(a)∗f(b)f(ab)=f(a)∗f(b)的话,我们称f(n)f(n)为完全积性函数。
常见的积性函数有:
因子个数函数d(n)d(n),因子和函数σ(n)σ(n),二元函数gcd(a,b)gcd(a,b),欧拉函数φ(n)φ(n),莫比乌斯函数u(n)u(n)。
完全积性函数有:
元函数e(n)=[n==1]e(n)=[n==1],恒等函数I(n)=1I(n)=1,单位函数id(n)=nid(n)=n。
我们来看看积性函数的应用:
如果f(x)f(x)为积性函数且n=∑ti=1peiin=∑i=1tpiei,那么可以得到f(n)=∏ti=1f(peii)f(n)=∏i=1tf(piei),如果f(x)为完全积性函数,那么我们还可以进一步得到f(n)=∏ti=1f(pi)eif(n)=∏i=1tf(pi)ei。
举个简单的例子:因子个数d(n)=∏ti=1d(peii)=∏ti=1(ei+1)d(n)=∏i=1td(piei)=∏i=1t(ei+1),因子和函数σ(n)=∏ti=1σ(peii)=∏ti=1piei+1−1pi−1σ(n)=∏i=1tσ(piei)=∏i=1tpiei+1−1pi−1。
积性函数还可以用来进行线性筛从而得到很多数论函数的值~
比如下面的欧拉函数:
可以知道当i%p==0i%p==0时,φ(i∗p)=φ(i)∗pφ(i∗p)=φ(i)∗p,而当i%p!=0i%p!=0时,有
φ(i∗p)=φ(i)∗φ(p)=φ(i)∗(p−1)φ(i∗p)=φ(i)∗φ(p)=φ(i)∗(p−1)
。
const int N = 1e6 + 5;
bool vis[N];
int phi[N], p[N], cnt = 0;
void seive() {
cnt = 0;
phi[1] = 1;
for (int i = 2; i < N; i++) {
if (!vis[i]) p[cnt++] = i, phi[i] = i - 1;
for (int j = 0; j < cnt; j++) {
int s = i * p[j];
if (s > N) break;
vis[s] = 1;
if (i % p[j] == 0) {
phi[s] = p[j] * phi[i];
break;
}
phi[s] = (p[j] - 1) * phi[i];
}
}
}
积性函数的和也是积性函数,积性函数前缀和是一个常见的问题,常常需要低于线性时间内解决。
狄利克雷卷积
狄利克雷卷积是解决积性函数前缀和问题的一个重要工具。
定义
对两个算术函数f, g,定义其Dirichlet卷积为新函数f * g,满足(f∗g)(n)=∑d|nf(d)g(nd)(f∗g)(n)=∑d|nf(d)g(nd)。
狄利克雷卷积满足以下定律:
若f,g均为积性函数,则f*g也是积性函数。
我们可以O(nlogn)预处理两个函数的Dirichlet卷积。
LL f[N], g[N], h[N];
void calc(int n) {
for (int i = 1; i * i <= n; i++) {
h[i * i] += f[i] * g[i];
for (int j = i + 1; i * j <= n; j++) h[i * j] += f[i] * g[j] + f[j] * g[i];
}
}
举例
很多数论函数都可以用狄利克雷卷积来表示:
①约数个数函数d(n)=(I∗I)(n)d(n)=(I∗I)(n);
②约数和函数d(n)=(I∗id)(n)d(n)=(I∗id)(n);
③∑d|nu(d)=[n==1]∑d|nu(d)=[n==1],得到I∗u=eI∗u=e。
④∑d|nφ(d)=n∑d|nφ(d)=n,得到I∗φ=idI∗φ=id。
现在我们可以通过狄利克雷卷积来证明莫比乌斯反演了:
如果F(n)=∑d|nf(n)F(n)=∑d|nf(n),那么F = I*f,那么可以知道F*u = I*f*u=f*(I*u)=f*e=f,所以f(n)=∑d|nu(nd)F(d)f(n)=∑d|nu(nd)F(d)。
应用
设f(n)为一个数论函数,需要计算S(n)=∑ni=1f(i)S(n)=∑i=1nf(i),通常n>=1e9。
要解决上述问题,我们可以构造一个S(n)S(n)关于S(ni)S(ni)的递推式。
找到一个合适的数论函数g(n),
∑i=1n∑d|if(d)g(id)=∑i=1ng(i)∑j=1nif(j)=∑i=1ng(i)S(ni)∑i=1n∑d|if(d)g(id)=∑i=1ng(i)∑j=1nif(j)=∑i=1ng(i)S(ni)
这样,可以知道g(1)S(n)=∑ni=1(f∗g)(i)−∑ni=2g(i)∗S(ni)g(1)S(n)=∑i=1n(f∗g)(i)−∑i=2ng(i)∗S(ni)。
构造的卷积函数必须容易求得前缀和,这样我们可以记忆化地计算S(n),因为nini是分段的,可以计算出复杂度为O(n34)O(n34),只要我们预处理S函数前2323的值,复杂度就可以达到O(n23)O(n23)
上述的办法就是传说中的杜教筛了~
如果找不到适合的函数g(n),那么可以采用一种复杂度为O(n34logn)O(n34logn)的办法来实现求和,详见任之州的论文。
下面给出求积性函数前缀和时常用到的一些公式和结论:
①∑d|nu(d)=[ns==1]∑d|nu(d)=[n==1]。
②∑d|nφ(d)=n∑d|nφ(d)=n。
③∑ni=1i∗[(i,n)==1]=n∗φ(n)+[n==1]2∑i=1ni∗[(i,n)==1]=n∗φ(n)+[n==1]2。
④d(n2)=∑d|n2w(d)=∑d|n∑i|du2(i)d(n2)=∑d|n2w(d)=∑d|n∑i|du2(i),w(d)表示d的不同质因子的个数。
⑤d(n∗m)=∑i|n∑j|m[(i,j)==1]。d(n∗m)=∑i|n∑j|m[(i,j)==1]。
⑥
∑i=1n[(i,n)==1]∗(i−1,n)=∑d|n∑i=1n[(i,n)==1]∗[d|(i−1)]=∑d|nφ(d)∗φ(n)φ(d)=d(n)∗φ(n)。∑i=1n[(i,n)==1]∗(i−1,n)=∑d|n∑i=1n[(i,n)==1]∗[d|(i−1)]=∑d|nφ(d)∗φ(n)φ(d)=d(n)∗φ(n)。
。
⑦d|i∗j等价于d(i,d)|jd|i∗j等价于d(i,d)|j
上面的几个东西有些是很显然的,但是还是写出来放在一起好了,至于第六个本人能力过弱不知如何证明。。。
求积性函数前缀和时常用到的一些公式和结论:
①∑d|nu(d)=[n==1]∑d|nu(d)=[n==1]。
②∑d|nφ(d)=n∑d|nφ(d)=n。
③∑ni=1i∗[(i,n)==1]=n∗φ(n)+[n==1]2∑i=1ni∗[(i,n)==1]=n∗φ(n)+[n==1]2。
④d(n2)=∑d|n2w(d)=∑d|n∑i|du2(i)d(n2)=∑d|n2w(d)=∑d|n∑i|du2(i),w(d)表示d的不同质因子的个数。
⑤d(n∗m)=∑i|n∑j|m[(i,j)==1]。d(n∗m)=∑i|n∑j|m[(i,j)==1]。
⑥
∑i=1n[(i,n)==1]∗(i−1,n)=∑d|n∑i=1n[(i,n)==1]∗[d|(i−1)]=∑d|nφ(d)∗φ(n)φ(d)=d(n)∗φ(n)。∑i=1n[(i,n)==1]∗(i−1,n)=∑d|n∑i=1n[(i,n)==1]∗[d|(i−1)]=∑d|nφ(d)∗φ(n)φ(d)=d(n)∗φ(n)。
。
⑦d|i∗j等价于d(i,d)|jd|i∗j等价于d(i,d)|j
上面的几个东西有些是很显然的,但是还是写出来放在一起好了,至于第六个本人能力过弱不知如何证明。。。