数论 工具 线性筛

由于做莫反题需要大量的基础函数知识,于是有了这篇文章将我做到的函数都记录下来。

持续施工中。


约数和函数 \(\sigma\)

定义:\(\sigma(x)=\sum_{d|x} d\)

证其为积性函数:

\(x=\prod p_i^{a_i}\),设 \(d\)\(x\) 的质因数个数,那么发现:

\[\begin{aligned}\sigma(x)&=\sum_{i_1=0}^{a_1}\sum_{i_2=0}^{a_2}\cdots\sum_{i_d=0}^{a_d} p_1^{i_1}p_2^{i_2}\cdots p_d^{i_d}\\&=\sum_{i_1=0}^{a_1} p_1^{i_1}\sum_{i_2=0}^{a_2}p_2^{i_2}\cdots \sum_{i_d=0}^{a_d}p_d^{i_d}\\&=\prod_{i=1}^d \sum_{j=0}^{a_i}p_i^j \end{aligned} \]

我们发现当 \(d=1\) 时,\(\sigma(x)=\sum_{i=0}^{a_1} p_1^i\),即上述式子可以转化为:

\[\sigma(x)=\prod_{i=1}^d \sigma(p_i^{a_i}) \]

推广可得:

\[\sigma(a\times b)=\sigma(a)\sigma(b)\quad gcd(a,b)=1 \]

证毕。

接下来考虑如何线性筛。考虑我们当前的数 \(i\),令其最小的质因子为 \(p\)。由积性函数可知,我们需要先使两个数 \(a,b\) 互质,再由 \(\sigma(a\times b)=\sigma(a)\sigma(b)\) 转移。那么我们若想从 \(\sigma(i)\) 转移至 \(\sigma(i\times p)\),需要先将 \(i\) 中所有的质因子 \(p\) 除掉,转移式子即为 \(\sigma(i\times p)=\sigma(\frac{i}{p^{a_1}})\sigma(p^{a_1+1})\)。用 \(low_i\) 维护 \(\sigma(p^{a_1})\),那么 \(low\) 的转移有 \(low_{i\times p}=low_{i}\times p+1\),此时 \(\sigma\) 的转移有 \(\sigma(i\times p)=\frac{\sigma(i)}{low_i}\times low_{i\times p}\)

实现中,当出现 \(i \mod p_j=0\) 的情况就用上述方法转移,否则 \(\sigma_{i\times p_j}\) 直接用积性函数性质求,\(low_{i\times p_j}\) 的值等于 \(\sigma(p_j)\),因为如果此时 \(i\) 中有比 \(p_j\) 更小的质因子的话,早在前面就会被筛出来然后 break 掉。

因为 \(\sigma\)\(o\) 挺像,所以在代码中 \(o_i\) 表示 \(\sigma(i)\)

int pri[N],tot,o[N],low[N];
bool vis[N];
void Wprepare()
{
	o[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!vis[i]) pri[++tot]=i,o[i]=low[i]=i+1;
		for(int j=1;j<=tot;j++)
		{
			if(i*pri[j]>N) break;
			vis[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				low[i*pri[j]]=low[i]*pri[j]+1;
				o[i*pri[j]]=o[i]/low[i]*low[i*pri[j]];
				break;
			}
			low[i*pri[j]]=pri[j]+1;
			o[i*pri[j]]=o[i]*o[pri[j]];
		}
	}
}

例题是 [SDOI2014]数表,莫反部分不难,难点在理解如何筛约数和以及动态维护某个函数前缀和,练习 \(\sigma\) 函数的好题。

posted @ 2024-09-13 10:58  DrRatio  阅读(25)  评论(0编辑  收藏  举报