常见积性函数线性筛

线性筛素数

比埃氏筛更优的线性筛,线性体现在每个数会被它最小的质因子筛掉,最小的质因子显然只有一个,每个数只会被访问一次,严格的线性复杂度。

一个数被访问是由枚举“除去它最小的质因子后的值 \(i\) ”,再乘上“最小质因子 \(prime[j]\)(以下简写为\(p[j]\))”实现的。

例如 \(8\) 是在 \(i=4,p[j]=2\) 时被访问的,\(12\) 是在 \(i=6,p[j]=2\) 时被访问的,\(9\)\(i=3,p[j]=3\) (可以看到需要先把 \(i\) 加入质数中再枚举质数)。

若当前的 \(i\) 没有被访问,说明 \(i\) 最小的质因子是它本身,显然 \(i\) 就是个质数。

为了保证当前质因子是乘积的最小质因子,当 \(i\%p[j]==0\) 时停止对 \(j\) 的枚举,因为此时 \(i\) 中保证有质因子 \(p[j]\),当 \(p[j]\) 继续增大时,\(i\)\(p[j]\) 乘积的最小质因子不会再是 \(p[j]\),而是 \(i\) 中的最小质因子。

for(int i=2; i<=n; i++) {
	if(!vis[i]) p[++cnt] = i;
	for(int j=1; j<=cnt && i*p[j]<=n; j++) {
		vis[ i*p[j] ] = 1;
		if(i%p[j]==0) break;    //与埃氏筛唯一的区别
	}
}

积性函数

积性函数是指当 \(a\)\(b\) 互质时满足 \(f(ab)=f(a)*f(b)\) 的函数。常见的像欧拉函数 \(\varphi\) ,约数个数 \(d\), 约数和 \(\sigma\),莫比乌斯函数 \(\mu\)。所有的积性函数都能线筛(大概)。

我们在线性筛时,每个数是由 \(i\)\(p[j]\) 乘起来得到的。

\(i\%p[j]\ne 0\),因为 \(p[j]\) 是质数所以 \(i\)\(p[j]\) 互质,\(f( i*p[j] )=f(i)*f(p[j])\)

\(i\%p[j]=0\),我们一般需要考虑这个数中最小的质因子 \(p[j]\) 的出现次数对它函数值的影响,一般需要另设一个函数 \(g\) 来记录最小质因子的出现次数,当 \(i\%p[j]=0\) 时,\(g[i*p[j]]=g[i]+1\),而 \(f\) 值就要具体情况具体分析了。

欧拉函数 \(\varphi\) 线筛

欧拉函数 \(\varphi\),表示比 \(x\) 小的与 \(x\) 互质的数的个数,求单个的话是 \(sqrt\) 复杂度的 (如果忽略预处理的复杂度就是 \(sqrt / log\))。

\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),则\(\varphi(x)=x\prod\limits_{i=1}^n\dfrac {p_i-1} {p_i}\)

这个很好证,就是考虑每个质因子对 \(\varphi\) 值的贡献。

\(\varphi\) 是积性函数也就显然了,因为互质的两个数没有相同的 \(p\),两个数的质因子贡献互不影响。

\(i\%p[j]\ne 0\)\(\varphi(i*p[j])=\varphi(i)*\varphi(j)=\varphi(i)*(p[j]-1)\)

\(i\%p[j]=0\) ,由于指数 \(k_j\) 不会对答案造成影响,而 \(p[j]\) 已经对 \(i\) 产生过贡献,所以直接乘上 \(p[j]\) 即可,\(\varphi(i*p[j])=\varphi(i)*p[j]\)

for(int i=2; i<=n; i++) {
	if(!vis[i]) {
		p[++cnt] = i;
		phi[i] = i-1;
	}
	for(int j=1; j<=cnt && i*p[j]<=n; j++) {
		vis[ i*p[j] ] = 1;
		if(i%p[j]==0) {
			phi[ i*p[j] ] = phi[i] * p[j];
			break;
		}
		phi[ i*p[j] ] = phi[i] * (p[j]-1);
	}
}

约数个数 \(d\) 线筛

一个显然的结论:若\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),则\(d(x)=\prod\limits_{i=1}^n(k_i+1)\)

\(x\)\(y\) 互质,那么 \(x\)\(y\) 没有相同的质因子,显然\(d(xy)=d(x)*d(y)\),所以 \(d\) 是个积性函数,考虑同样的线筛方式。

\(i\%p[j]\ne 0\)\(d(i*p[j])=d(i)*d(p[j])=d(i)*2\)

\(i\%p[j]=0\) ,相当于 \(p[j]\) 的指数从 \(k_j\) 变成了 \(k_j+1\),辣么它的d就从一大坨乘上 \(k_j+1\) 变成一大坨乘上 \(k_j+2\)

所以有必要开一个 \(g\) 记录最小质因子出现次数,\(g[ i*p[j] ]=g[i]+1,d[i*p[j]]=d[i]/(g[i]+1)*(g[i]+2)\)。记得当 \(i\) 是质数时,\(d[i]=2,g[i]=1\)

d[1] = 1;
for(int i=2; i<=n; i++) {
    if(!vis[i]) {
        p[++cnt] = i;
        d[i] = 2; g[i] = 1;
    }
    for(int j=1; j<=cnt && i*p[j]<=n; j++) {
        vis[ i*p[j] ] = 1;
        if(i%p[j]==0) {
            d[ i*p[j] ] = d[i] / (g[i]+1) * (g[i]+2);
            g[ i*p[j] ] = g[i] + 1;
            break;
        }
        d[ i*p[j] ] = d[i] * 2;
        g[ i*p[j] ] = 1;
    }
}

约数和 \(\sigma\) 线筛

\(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\),那么有一个结论是

\(\sigma(x)=(1+p_1+{p_1}^2+...+{p_1}^{k_1})*(1+p_2+{p_2}^2+...+{p_2}^{k_2})*...*(1+p_n+{p_n}^2+...+{p_n}^{k_n})\)

简写就是 \(\sigma(x)=\prod\limits_{i=1}^n\sum\limits_{j=0}^{k_i}{p_i}^j\)

因为从每个括号中各选一项相乘得到的数一定是 \(x\) 的约数,而且是一一对应的,将所有项加起来就是约数和了。

由于互质的两个数质因子互不相同,联乘可以叠在一起,所以 \(\sigma\) 是积性函数。

\(i\%p[j]\ne 0\)\(\sigma(i*p[j])=\sigma(i)*\sigma(p[j])=\sigma(i)*(p[j]+1)\)

\(i\%p[j]=0\), 相当于将 $p_j $ 的指数 \(k_j\) 加一,则 \(p_j\) 对答案的贡献从 \((1+p_j+{p_j}^2+...+{p_j}^{k_j})\) 变为 \((1+p_j+{p_j}^2+...+{p_j}^{k_j}+{p_j}^{k_j+1})\),而不会对其他质因数造成影响。

\(g[i]\) 表示 \(i\) 中不能被 \(p_j\)\(i\) 的最小质因子)整除的约数的和,
\(\sigma(i)=(1+p_j+{p_j}^2+...+{p_j}^{k_j})*g[i]\)

\(\sigma(i*p[j])=(1+p_j+{p_j}^2+...+{p_j}^{k_j})*p_j*g[i]+g[i]\)
\(=(1+p_j+{p_j}^2+...+{p_j}^{k_j}+{p_j}^{k_j+1})*g[i]=\sigma(i)*p[j]+g[i]\)

然后注意 \(g\) 数组的赋值:当 \(p[j]\)\(i\) 的质因子时,\(g[i*p[j]]=g[i]\),否则 \(g[i*p[j]]=f[i]\),这个应该好理解。

代码中 \(\sigma\)\(f\) 表示:

f[1] = 1;
for(int i=2; i<=n; i++) {
	if(!vis[i]) {
		p[++cnt] = i;
		f[i] = i+1; g[i] = 1;
	}
	for(int j=1; j<=cnt && i*p[j]<=n; j++) {
		vis[ i*p[j] ] = 1;
		if(i%p[j]==0) {
			f[ i*p[j] ] = f[i] * p[j] + g[i];
			g[ i*p[j] ] = g[i];
			break;
		}
		f[ i*p[j] ] = f[i] * (p[j]+1);
		g[ i*p[j] ] = f[i];
	}
}

2020.06.06 update:

这玩意今天模拟赛出了个应用。。谔谔。

大概是问你 \(\sum\limits_{i=1}^n n\%i\),这玩意儿显然可以用整除分块来根号复杂度做,但是这题多组询问的 \(T\) 达到了 \(1e6\),那么就找规律。

发现这个数列:\(\sum\limits_{i=1}^n \left\lfloor\frac ni\right\rfloor i\)\(1,4,8,15,21...\)

和刚刚筛出来的 \(\sigma:\)\(1,3,4,7,6...\)

为啥是这个前缀和呢? \(@159\) 给出了理性的证明:\(\left\lfloor\frac ni\right\rfloor i\) 可以等价于小于等于 \(n\) 的数中有多少个数是 \(i\) 的倍数,再乘个
\(i\),把所有 \(i\) 的倍数加起来,就是把所有数的因数和加起来。

莫比乌斯函数 \(\mu\) 线筛

想做反演题你得先把它筛出来。

莫比乌斯函数是 人类自己捏出来的 一个容斥系数函数,来个通俗一点的定义:

若x=1,则\(\mu(x)=1\);当 \(x>1\) 时分解 \(x=p_1^{k1}*p_2^{k2}*p_3^{k3}...p_n^{kn}\)

\(k_1=k_2=k_3=...=k_n=1\)\(\mu(x)=(-1)^n\)

否则\(\mu(x)=0\)


\(\mu\) 显然是个积性函数,感性证明:

\(x\)\(y\) 互质,若 \(\mu(x)=0\)\(\mu(y)=0\),说明有一个质因子的指数大于1,那么xy那个质因子指数也大于1,\(\mu(xy)=\mu(x)*\mu(y)=0\)

\(x\ne 0\)\(y\ne 0\) 时,\(\mu(x)=(-1)^{n_x},\mu(y)=(-1)^{n_y},\mu(xy)=(-1)^{n_x+n_y}=\mu(x)*\mu(y)\)

考虑线筛:

\(i\%p[j]\ne0\)\(\mu(i*p[j])=\mu(i)*\mu(p[j])=-\mu(i)\),因为质数的 \(\mu\) 都是 \(-1\)

\(i\%p[j]=0\),说明 \(p[j]\) 这个质因子将在 \(i*p[j]\) 出现两次,那\(\mu(i*p[j])\) 就等于 \(0\) 呗。

mu[1] = 1;
for(int i=2; i<=n; i++) {
	if(!vis[i]) {
		p[++cnt] = i;
		mu[i] = -1;
	}
	for(int j=1; j<=cnt && i*p[j]<=n; j++) {
		vis[ i*p[j] ] = 1;
		if(i%p[j]==0) break;    //mu[i*p[j]]=0;就没必要写了。
		mu[ i*p[j] ] = -mu[i];
	}
}

参考:

https://www.cnblogs.com/cjoieryl/p/8268373.html

https://www.cnblogs.com/zhoushuyu/p/8275530.html

https://www.cnblogs.com/TheRoadToTheGold/p/8228969.html

posted @ 2020-04-20 23:18  maple276  阅读(256)  评论(0编辑  收藏  举报