杜教筛的学习笔记

写这篇文章帮助自己回忆一下之前学习过的东西以及一些常见的反演套路吧\(QWQ\)

首先简单介绍一下积性函数:

\(\phi,\mu,d...\)这些都是积性函数

定义是:若\(f(x*y) = f(x)*f(y) \quad (gcd(x,y)==1)\)

\(f(x)\)是一个积性函数。

常见的积性函数:

\(\mu,\quad\phi,\quad d(\)因数个数函数\()\),因数个数和函数等......

如果对于任意的\(x,y\)都有:\(f(x*y)=f(x)*f(y)\)

则称此函数为完全积性函数:

常见有:

\(Id(x)= x, \varepsilon(x) = (x==1), I(x) = 1\)

接下来是杜教筛的重头戏:

迪利克雷卷积

定义两个积性函数的迪利克雷卷积为:

\((f*g)(x) = \sum_{d|x}f(x)*g(\dfrac{x}{d})\)

可以证明,(虽然我不会),两个积性函数的迪利克雷卷积得到的函数仍然是一个积性函数

那么我们来考虑原来要求的东西

比如要求\(\mu(x)\)的前缀和。

那么我们可以考虑找一个函数和它卷起来。

根据莫比乌斯函数的性质可以得到:\(\mu*I = \varepsilon\)

当然还有一些用的比较多的:(下面会给出证明)

证明.ing

\((\phi*I)(x) = Id(x)\)

\((\mu*Id)(x) = \phi(x)\)

简单证明一下:

\((\phi*I)(x) = Id(x)\)

其实就是:

\(\sum_{i|x} \phi(i) * I(x/i)\), 其中\(I(x/i) = 1\)

也就是:

\(\sum_{i|x}\phi(i)\)

我们可以考虑欧拉函数的实际意义,\(\phi(i)\)表示为\(1-i\)中与\(i\)互质的数的个数。

那么我们不妨假设\(n\)的因子依次为:\(1,p1,p2.......n(p_i\)不一定为质数!\()\)

接着我们考虑对于\(gcd(n, a) = 1\), 其中,满足这个式子的\(a\)的个数就是\(\phi(n)\)

考虑\(gcd(n, a) = d\)的时候,那么\(d|n\)\(d|a\),所以其实这个式子就是: \(gcd(n/d, a/d) = 1\) ,满足这个式子的\(a\)的个数为\(:\phi(n/d)\)个。

不难发现:\(\sum_{i|x}\phi(i) = \sum_{i|x} \phi(x/i)\) 而这个式子的答案的意义为:与\(x\)\(gcd\)依次为\(1,p_1...p_n\)的数的个数,这个数的个数为\(x\)

所以:\(\sum_{i|x}\phi(i) = x = Id(x)\)

接下来我们证明这个:

\((\mu*Id)(x) = \phi(x)\)

不难发现,卷积其实具有交换律。\((f*g*h)(x) = (f*h*g)(x)\)

所以:\(\mu*Id*I = \mu*I*Id = \phi*I\)

即:\(\mu*Id*I = \phi* I\),故:\(\mu*Id = \phi\)

完毕撒花

接下来我们回到题目,我们要求这两个东西:

\[\sum_{i=1}^n\mu(i) \]

\[\sum_{i=1}^n\phi(i) \]

我们可以先找一个东西和这个函数卷起来。(以第一个前缀和为例)

\(\mu*I = \varepsilon\)

所以这两个东西卷在一起的前缀和即:

\[\sum_{i=1}^n\sum_{d|i}\mu(d)*I(\dfrac{i}{d}) \]

稍微改写一下:

\[\sum_{d=1}^nI(d)*\sum_{k=1}^{\frac{n}{k}}\mu(k) \]

然而实际上我们有:\(\mu*I=\varepsilon\)

所以原来的式子就是:

\[\sum_{i=1}^n\varepsilon(i) = 1 \]

现在我们怎么根据这个式子得到莫比乌斯函数的前缀和呢?

\[\sum_{i=1}^n\mu(i) = \sum_{d=1}^nI(d)*\sum_{k=1}^{\frac{n}{k}}\mu(k) - \sum_{d=2}^nI(d)*\sum_{k=1}^{\frac{n}{k}}\mu(k) \]

因为\(d=2\)以后的地方都会被抵消掉。

我们可以再记\(G(x) = \sum_{i=1}^x\mu(i)\)

所以我们再把原式化简得到:

\(G(n) = 1(\)\(\sum_{i=1}\varepsilon(i)) - \sum_{d=2}^nI(d)*G(\frac{n}{k})\)

后面那一坨可以整除分块\(+\)递归处理

\(\sum_{i=1}^n\phi(i)\)也可以类似处理。

只不过要利用\(\phi*I = Id\)的性质。

当然还有一些其他的利用比如:\((id*id = d)\)\(id\)函数卷上\(id\)函数即为\(d\)

代码:

#include<bits/stdc++.h>
using namespace std;
int read() {
	char cc = getchar(); int cn = 0, flus = 1;
	while(cc < '0' || cc > '9') {  if( cc == '-' ) flus = -flus;  cc = getchar();  }
	while(cc >= '0' && cc <= '9')  cn = cn * 10 + cc - '0', cc = getchar();
	return cn * flus;
}
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define LL long long
#define il inline
const int N = 5000000 + 5;
LL phi[N], f2[N];
int u[N], f1[N], pr[N], top, maxn;
bool isp[N];
map< int, LL > map1, map2;
il void init() {
	f1[1] = f2[1] = u[1] = phi[1] = 1;
	maxn = N - 5;
	rep( i, 2, maxn ) {
		if( !isp[i] ) pr[++ top] = i, u[i] = -1, phi[i] = i - 1;
		rep( j, 1, top ) {
			int p = pr[j];
			if( p * i > maxn ) break;
			isp[i * p] = 1;
			if( i % p == 0 ) {  phi[p * i] = p * phi[i]; break; }
			phi[i * p] = phi[i] * phi[p], u[i * p] = u[i] * u[p];
		}
		f1[i] = f1[i - 1] + u[i], f2[i] = f2[i - 1] + phi[i];
	}
} 
il int solve1( int x ) { //mu
	if( x <= maxn ) return f1[x];
	if( map1[x] ) return map1[x];
	int Id = 1, l, r;
	for( l = 2; l <= x; l = r + 1 ) {
		r = ( x / ( x / l ) );
		Id -= ( r - l + 1 ) * solve1( x / l );
	}
	return map1[x] = Id;
}
il LL solve2( LL x ) {
	if( x <= maxn ) return f2[x];
	if( map2[x] ) return map2[x];
	LL Id = ( 1ll * x * ( x + 1ll ) / 2ll );
	int l, r;
	for( l = 2; l <= x; l = r + 1 ) {
		r = ( x / ( x / l ) );
		Id -= 1ll * ( r - l + 1 ) * solve2( x / l );
	}
	return map2[x] = Id;
}
signed main()
{
	init(); int T = read(), x;
	while( T -- ) {
		x = read();
		printf("%lld %d\n", solve2(x), solve1(x) );
	}
	return 0;
}

注意\(\phi\)函数的前缀和要开long long

posted @ 2019-03-25 21:33  Soulist  阅读(239)  评论(0编辑  收藏  举报