Min_25 筛 学习笔记

前言

很久之前就应该学的,但是因为太鸽了咕到了现在,而且估计以后也会忘,所以写一篇符合自己理解方式的学习笔记,估计对萌新的学习没有什么帮助,所以这边先放几个学习链接。

wucstdio

yyb

AThousandMoon

Min_25 筛的关键想法:因为所有合数都有至少一个小于 \(\sqrt{n}\) 的因子,所以我们应当通过预处理的方式来处理出质数处的前缀和!

关于本文中一些符号的定义

  • \(lpf(i)\)表示\(i\)的最小质因子。

  • 在不进行特殊说明的情况下,\(p\)视为质数。

Min_25 筛是干什么的?

Min_25 筛是用来求一个积性函数前缀和的东西。也就是求解这样的式子:\(\sum_{i=1}^{n} f(i)\)(其中\(f\)是积性函数)

可以使用 Min_25 筛的前提是什么?

要求积性函数\(f\)\(f(p^k)\)的位置上能够快速地求出来,换一句话说,就是\(f(p^k)\)是一个次数比较低的多项式。

怎么进行 Min_25 筛?

假设我们需要求\(\sum_{i=1}^n f(i)\)

那么,我们将\(i\)按照其是否为质数分类,原式则可以变为:\(\sum_{1\leq p \leq n} f(p) +\sum_{i=1}^{n} [i\, is\, not\, a\, prime]f(i)\)

前面的先放一边,因为可以直接算出点值,考虑后面的合数部分怎么化。

因为\(f\)是积性函数,所以枚举质数,接下来可以把式子化成这样(定义\(lpf(i)\)\(i\)的最小质因子):
\(\sum_{1\leq p \leq n} f(p) + \sum_{p^e \leq n} f(p^e) (\sum_{i=1 \& lpf(i)>p}^{\left \lfloor \frac{n}{p^e} \right \rfloor} f(i))\)

但是,我们注意到如果是合数的话,那么它的最小质因子一定小于\(\sqrt{n}\)

所以式子就变成了这样:
\(\sum_{1\leq p \leq n} f(p) + \sum_{p^e \leq n\& 1\leq p \leq \sqrt{n} } f(p^e) (\sum_{i=1 \& lpf(i)>p}^{\left \lfloor \frac{n}{p^e} \right \rfloor} f(i))\)

接下来我们要分两个步骤处理。

步骤 1

为了解决这个问题,我们设计一个函数\(g(n,j)\)表示在\(n\)以内质数或者最小质因子大于第\(j\)个质数的数字的\(h\)函数值的和,当然,这个\(h\)函数并不是原来的\(f\)函数,只是一个在\(p^k\)处的取值和\(f\)相同或者相似的函数,因为我们的前提中\(f(p^k)\)是一个关于\(p^k\)的低阶多项式,所以\(h\)一般会设为\(h(i)=i^k\)这种形式(注:可以设多个\(h\),最后加起来)。

下面用\(p_j\)表示第\(j\)个质数。

那么,用数学式子来表示的话,就是\(g(n,j)=\sum_{i=1}^{n} [i\, is\, a\, prime\, or\, lpf(i) > p_j] i^k\)

我们考虑用 DP 的方法来解决这个问题。

考虑从\(g(n,j-1)\)转移到\(g(n,j)\)

很明显,需要减去一些数,因为有一些数已经不满足条件了,考虑这些不满足条件的数是哪些。

很明显,所有不满足条件的数就是最小质因子是\(p_j\)的合数。

那么,这些数我们提取除一个\(p_j\)出来,我们就可以写出这么一个式子来表示这些数对原来答案的贡献:\(p_j^k\cdot (g(\frac{n}{p_j},j-1)- g(p_{j-1},j-1))\),后面减去\(g(p_{j-1},j-1)\)是因为我们要把原来的质数给去掉。

接下来我们就可以列出转移的式子了。

\(g(n,j)=g(n,j-1)-p_j^k\cdot (g(\frac{n}{p_j},j-1)- g(p_{j-1},j-1))\)

这个式子看起来计算是\(O(n*|P|)\)\(|P|\)表示\(n\)以内的质数个数),但是我们发现\(\left \lfloor \frac{n}{x}\right \rfloor\)最多只有\(O(\sqrt{n})\)钟取值,所以可以离散化,那么就可以快速地计算出\(g(n,j)\)了,空间上的优化直接用滚动数组就可以了。

步骤 2

但是我们并不能通过\(g\)函数来找出我们所需要的答案。

所以我们仿照上面的思路,构造出一个\(S(n,k)\)函数表示从\(1\)\(n\)最小质因子大于\(p_k\)\(f\)函数值的和,那么,如果我们能够快速地求出\(S(n,0)\),这一道题就解决了。

同样是分质数和合数来考虑,合数也依然是枚举最小质因子。

\(S(n,k)=g(n,|P|)-g(p_{|P|},|P|)+\sum_{p_j^e\leq n \& j>k}\) \(f(p_{j}^{e}) (S(\left \lfloor \frac{n}{p_{j}^{e}} \right \rfloor ,j)+[e\neq 1])\)

好的,这样\(S\)就可以递归算了,时间复杂度分析我不会,据说是\(O(\frac{n^{\frac{3}{4}}}{\log n})\)

下面附上洛谷模板的代码:

#include <cmath>
#include <cstdio>
template<typename Elem>
void read(Elem &a){
	a=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		a=(a<<1)+(a<<3)+(c^48);
		c=getchar();
	}
}
template<typename Elem>
void write(Elem a){
	if(a<10){
		putchar(a+'0');
		return;
	}
	write(a/10);
	putchar(a%10+'0');
}
const int Maxn=200000;
const int Mod=1000000007;
typedef long long ll;
const int Inv_2=(Mod+1)>>1;
const int Inv_3=(Mod+1)/3;
ll n;
int sqr;
bool np[Maxn+5];
int p[Maxn+5],p_len;
int sp_1[Maxn+5],sp_2[Maxn+5];
int g_1[Maxn+5],g_2[Maxn+5];
int pos_1[Maxn+5],pos_2[Maxn+5];
ll w[Maxn+5];
int tot;
void init(){
	np[0]=np[1]=1;
	for(int i=2;i<=Maxn;i++){
		if(!np[i]){
			p[++p_len]=i;
			sp_1[p_len]=(sp_1[p_len-1]+i)%Mod;
			sp_2[p_len]=(sp_2[p_len-1]+1ll*i*i)%Mod;
		}
		for(int j=1,x;j<=p_len&&(x=i*p[j])<=Maxn;j++){
			np[x]=1;
			if(i%p[j]==0){
				break;
			}
		}
	}
}
int find_sum(ll x,int y){
	if(p[y]>=x){
		return 0;
	}
	int k=x<=sqr?pos_1[x]:pos_2[n/x];
	int ans=((g_2[k]-g_1[k]+Mod)%Mod-(sp_2[y]-sp_1[y]+Mod)%Mod+Mod)%Mod;
	for(int i=y+1;i<=p_len&&1ll*p[i]*p[i]<=x;i++){
		ll p_e=p[i];
		for(int j=1;p_e<=x;j++,p_e*=p[i]){
			int p_e_m=p_e%Mod;
			ans=(ans+1ll*p_e_m*(p_e_m-1)%Mod*(find_sum(x/p_e,i)+(j!=1)))%Mod;
		}
	}
	return ans;
}
int main(){
	init();
	read(n);
	sqr=(int)sqrt(n+0.0);
	for(ll i=1,j;i<=n;i=j+1){
		j=n/(n/i);
		w[++tot]=n/i;
		g_1[tot]=w[tot]%Mod;
		g_2[tot]=1ll*g_1[tot]*(g_1[tot]+1)/2%Mod*(2*g_1[tot]+1)%Mod*Inv_3%Mod-1;
		if(g_2[tot]<0){
			g_2[tot]+=Mod;
		}
		g_1[tot]=1ll*g_1[tot]*(g_1[tot]+1)/2%Mod-1;
		if(g_1[tot]<0){
			g_1[tot]+=Mod;
		}
		if(n/i<=sqr){
			pos_1[n/i]=tot;
		}
		else{
			pos_2[n/(n/i)]=tot;
		}
	}
	for(int i=1;i<=p_len;i++){
		for(int j=1;j<=tot&&1ll*p[i]*p[i]<=w[j];j++){
			int k=w[j]/p[i]<=sqr?pos_1[w[j]/p[i]]:pos_2[n/(w[j]/p[i])];
			g_1[j]=(g_1[j]-1ll*p[i]*(g_1[k]-sp_1[i-1]+Mod)%Mod+Mod)%Mod;
			g_2[j]=(g_2[j]-1ll*p[i]*p[i]%Mod*(g_2[k]-sp_2[i-1]+Mod)%Mod+Mod)%Mod;
		}
	}
	write((find_sum(n,0)+1)%Mod);
	putchar('\n');
	return 0;
}
posted @ 2020-07-07 13:07  with_hope  阅读(194)  评论(0编辑  收藏  举报