Min_25 筛

Min_25 筛 2020.9.24

这阅读量都14k了就离谱。

本来打算把之前的内容删了重写的,纠结了一下还是决定保留下来,大家就当看了图个乐。

Min_25筛是干啥的

\(O(\frac{n^{0.75}}{\log n})\)求积性函数前缀和。

咋做啊

大致分两步:

  • 对所有\(x=\lfloor\frac nd\rfloor\)筛出不超过\(x\)的所有质数的\(f\)值之和
  • 求解原问题,即所有\(x=\lfloor\frac nd\rfloor\)筛出不超过\(x\)的所有数的\(f\)值之和

为了方便,我们定义\(g(i)(i \in \mathbb{N})\)表示把\(i\)当成质数计算\(f(i)\)得到的结果。

定义\(pri_j\)表示第\(j\)小的质数,\(sum_j=\sum_{k=1}^{j}f(pri_k)=\sum_{k=1}^jg(pri_k)\)

(因为一个合数的最小质因子小于其开根号,因此预处理质数时只需要考虑\(\le \sqrt n\)的质数,记\(tot\)表示这部分质数的数量。)

Min_25筛对被求解的积性函数\(f\)有一定的要求,最重要的一点是当给出\(\sum_{i \in S}g(i)\)时,能够快速的计算出\(\sum_{i \in S}g(i \times pri_j)\)

第一步

我们记\(G(i,j)(i = \lfloor\frac nd\rfloor)\)表示\(i\)以内所有质数以及最小质因子\(>pri_j\)的合数的\(g\)之和。

初始时\(G(i,0)=\sum_{k=2}^{i}g(k)\)

从小到大枚举质因子\(pri_j\),把最小质因子恰好为\(pri_j\)的那些合数的\(g\)\(G\)中去掉。枚举\(\ge pri_j^2\)的所有\(i\)\(G(i,j)\)需要去掉的部分是\(G(\lfloor\frac{i}{pri_j}\rfloor,j-1)-sum_{j-1}\)并把\(\sum g(i)\)变成\(\sum_{g(i \times pri_j)}\)

最终求出\(G(i,tot)\)就是\(i\)以内所有质数的\(f\)之和。

第二步

我们记\(F(i,j)(i = \lfloor\frac nd\rfloor)\)表示\(i\)以内所有质数以及最小质因子\(>pri_j\)的合数的\(f\)之和。

初始时\(F(i,tot)=G(i,tot)\)

从大到小枚举\(pri_j\),把最小质因子恰好为\(pri_j\)的那些合数的\(f\)加到\(F\)中去。枚举\(\ge pri_j^2\)的所有\(i\),再额外枚举\(pri_j^{e+1}\le i\)的指数\(e\),把\(F(\lfloor\frac{i}{pri_j^e}\rfloor,j)\times f(pri_j^e)+f(pri_j^{e+1})\)加进\(F(i,j-1)\)

最后\(F(i,0)\)就是答案了!!1

时间复杂度

要是写得不对的话可以来喷我。

对于一个数\(x\),满足\(p^2 \le x\)的质数\(p\)的数量是\(O(\frac{\sqrt x}{\log \sqrt x})\)

考虑第一步的复杂度。考虑每个\(i=\lfloor\frac nd\rfloor\)被多少个质数枚举到了,有

\[T(n)=\sum_{x=1}^\sqrt{n}O(\frac{\sqrt x}{\log \sqrt x})+\sum_{x=1}^{\sqrt n}O(\frac{\sqrt {n/x}}{\log \sqrt {n/x}}) \]

\(\log\)都是同级的可以提出来(?)

\[\sum_{x=1}^\sqrt{n}O(\sqrt x)+\sum_{x=1}^{\sqrt n}O(\sqrt {n/x})\\\approx\int_{0}^{\sqrt n}\sqrt xdx+\int_{0}^{\sqrt n}\sqrt{n/x}dx\\=\frac{2}{3}n^{\frac{3}{4}}+2n^{\frac{3}{4}}\\=O(n^{0.75}) \]

所以总复杂度就是\(O(\frac{n^{0.75}}{\log n})\)了(迫真)


以下是原来写的。

yyb好神仙啊

干什么用的

可以在\(O(\frac{n^{\frac 34}}{\log n})\)的时间内求积性函数\(f(x)\)的前缀和。

别问我为什么是这个复杂度

要求\(f(p)\)是一个关于\(p\)的简单多项式,\(f(p^c)\)可以快速计算。

怎么做啊

首先我们需要对每个\(x=\lfloor\frac ni\rfloor\)求出\(\sum_{i=1}^x[i是质数]f(i)\)

怎么求呢?

先线性筛出\(\sqrt n\)范围内的质数,设\(P_j\)表示从小到大第\(j\)个质数。

\(g(n,j)=\sum_{i=1}^{n}[i \in P \ or\ \min(p)>P_j]f(i)\)

说人话就是:\(i\)是质数,或者\(i\)的最小质因子大于\(P_j\),把\(1-n\)内满足条件的\(f(i)\)加起来就是\(g(n,j)\)

这个东西的实际含义是什么呢?可以参考一下埃氏筛法的运行过程。

假设现在有\(n\)个数依次排开,第\(i\)个数是\(f(i)\),根据埃氏筛法的那套理论,每次选出一个质数,然后筛掉它的所有倍数。

会发现\(g(n,j)\)就是运行\(j\)次埃氏筛法后,没被筛掉的所有数之和加上所有的\(f(p)\)

我们要求的\(\sum_{i=1}^x[i是质数]f(i)\)其实就是\(g(x,|P|)\),其中\(|P|\)是质数集合的大小。

考虑\(g(n,j)\)的转移,分两种情况:

1、\(P_j^2>n\)。此时运行的第\(j\)次已经不会再筛掉任何数了(因为第\(j\)次运行中筛掉的最小的数是\(P_j^2\)),所以此时\(g(n,j)=g(n,j-1)\)

2、\(P_j^2\le n\)。这时候我们就要考虑哪些数被筛掉了。被筛掉的数一定含有质因子\(P_j\),且除掉\(P_j\)后最小的质因子会大于等于\(P_j\)。考虑减去\(f(P_j)\times g(\frac{n}{P_j},j-1)\),但在\(g(\frac{n}{P_j},j-1)\)中多减去了\(\sum_{i=1}^{j-1}f(P_i)\)这些最小质因子小于\(P_j\)的函数值,所以再把它们加上就好了。

所以总结起来就是:

\[g(n,j)=\begin{cases} g(n,j-1)&P_j^2\gt n\\ g(n,j-1)-f(P_j)[g(\frac{n}{P_j},j-1)-\sum_{i=1}^{j-1}f(P_i)]&P_j^2\le n\end{cases} \]

关于\(g(n,j)\)的初值问题:\(g(n,0)\)表示所有数的和,也就是把所有数都当作是质数带入\(f(p)\)的那个多项式中算出的结果。

因为最后只要求所有的\(g(x,|P|)\),所以在求的时候数组只开了一维。这样做的复杂度被证明是\(O(\frac{n^{\frac 34}}{\log n})\)的。

\(f(x)=1\)即求\(n\)以内的质数个数为例:

for (int i=1,j;i<=n;i=j+1){
	j=n/(n/i);w[++m]=n/i;
	if (w[m]<=Sqr) id1[w[m]]=m;
	else id2[n/w[m]]=m;
	g[m]=(w[m]-1)%mod;
}
for (int j=1;j<=tot;++j)
	for (int i=1;i<=m&&pri[j]*pri[j]<=w[i];++i){
		int k=(w[i]/pri[j]<=Sqr)?id1[w[i]/pri[j]]:id2[n/(w[i]/pri[j])];
		g[i]=(g[i]-g[k]+j-1)%mod;g[i]=(g[i]+mod)%mod;
	}

说了那么多你求出了啥?

现在我们已经对于\(x=\lfloor\frac ni\rfloor\)求出了\(\sum_{i=1}^x[i是质数]f(i)\)

我们设\(S(n,j)=\sum_{i=1}^n[\min(p)\ge P_j]f(i)\),也就是所有满足最小质因子大于等于\(P_j\)\(f\)值之和。

那么最终的答案就是\(S(n,1)+f(1)\)

鉴于质数的答案我们已经算出来了,是\(g(n,j)-\sum_{i=1}^{j-1}f(P_i)\)。(因为要保证最小质因子大于等于\(P_j\)所以要把小于它的质数减掉)

考虑合数。我们枚举这个合数的最小质因子及其出现次数,然后直接乘即可。

\[S(n,j)=g(n,j)-\sum_{i=1}^{j-1}f(P_i)+\sum_{k=j}^{P_k^2\le n}\sum_{e=1}^{P_k^{e+1}\le n}S(\frac{n}{P_k^e},k+1)\times f(P_k^e)+f(P_k^{e+1}) \]

然后这个的复杂度也被证明是\(O(\frac{n^{\frac 34}}{\log n})\)的。

举个栗子

loj6053简单的函数

定义积性函数\(f(p^c)=p\oplus c\),求其前\(n\)项和。

会发现除了\(2\)以外的质数都满足\(f(p)=p\oplus 1=p-1\),所以可以分别计算出\(g(x,|P|)=\sum_{i=1}^x[i是质数]i\)以及\(h(x,|P|)=\sum_{i=1}^x[i是质数]1\)

在处理\(S\)的时候,如果\(j=1\),就说明其中包含\(2\)这个因数,因此把答案\(+2\)即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define ll long long
const int N = 1e6+5;
const int mod = 1e9+7;
int Sqr,zhi[N],pri[N],sp[N],tot,m,id1[N],id2[N],g[N],h[N];
ll n,w[N];
void Sieve(int n){
	zhi[1]=1;
	for (int i=2;i<=n;++i){
		if (!zhi[i]) pri[++tot]=i,sp[tot]=(sp[tot-1]+i)%mod;
		for (int j=1;i*pri[j]<=n;++j){
			zhi[i*pri[j]]=1;
			if (i%pri[j]==0) break;
		}
	}
}
int S(ll x,int y){
	if (x<=1||pri[y]>x) return 0;
	int k=(x<=Sqr)?id1[x]:id2[n/x];
	int res=(1ll*g[k]-h[k]-sp[y-1]+y-1)%mod;res=(res+mod)%mod;
	if (y==1) res+=2;
	for (int i=y;i<=tot&&1ll*pri[i]*pri[i]<=x;++i){
		ll p1=pri[i],p2=1ll*pri[i]*pri[i];
		for (int e=1;p2<=x;++e,p1=p2,p2*=pri[i])
			(res+=(1ll*S(x/p1,i+1)*(pri[i]^e)%mod+(pri[i]^(e+1)))%mod)%=mod;
	}
	return res;
}
int main(){
	scanf("%lld",&n);
	Sqr=sqrt(n);Sieve(Sqr);
	for (ll i=1,j;i<=n;i=j+1){
		j=n/(n/i);w[++m]=n/i;
		if (w[m]<=Sqr) id1[w[m]]=m;
		else id2[n/w[m]]=m;
		h[m]=(w[m]-1)%mod;
		g[m]=((w[m]+2)%mod)*((w[m]-1)%mod)%mod;
		if (g[m]&1) g[m]+=mod;g[m]/=2;
	}
	for (int j=1;j<=tot;++j)
		for (int i=1;i<=m&&1ll*pri[j]*pri[j]<=w[i];++i){
			int k=(w[i]/pri[j]<=Sqr)?id1[w[i]/pri[j]]:id2[n/(w[i]/pri[j])];
			g[i]=(g[i]-1ll*pri[j]*(g[k]-sp[j-1])%mod)%mod;g[i]=(g[i]+mod)%mod;
			h[i]=(h[i]-h[k]+j-1)%mod;h[i]=(h[i]+mod)%mod;
		}
	printf("%d\n",S(n,1)+1);
	return 0;
}

posted @ 2018-06-15 14:56  租酥雨  阅读(18053)  评论(9编辑  收藏