参考博客
zsy
xuyixuan
litble
前置
min25就是一种能在低于线性复杂度求积性函数f(x)前缀和的筛法
复杂度大概为O(lognn43)(不会证,不过朱老大论文里有证明(看不懂))
要求f(x)是一个积性函数,而且对于素数p,f(p)和f(pk)可以快速得到
g
设P表示素数集合,Mink表示k的最小质因子
pri表示从小到大第i个素数
首先考虑求出∑i=1n[i∈P]f(i)
定义g(n,i)=∑j=1n[j∈PorMinj>pri]f(i)
仔细瞪一下这个函数
我们发现从g(n,i−1)到g(n,i)
我们就相当于做埃氏筛的过程,用pri把[1,n]所有还没有被筛去的pri的倍数筛去
实际上g(n,i)就是对[1,n]用前i个素数做一次埃氏筛后剩下的数f之和
现在考虑怎么从g(n,i−1)推到g(n,i)
首先如果pri2>n那显然不会有筛去任何数
否则考虑哪些数被删去了
一定是含质因子pi,而且除去pi后最小质因子一定大于pi
考虑其实也就是减去f(pi)×g(pin,i−1)
又由于我们把pr1~pri−1这些素数的f值减去了,所以要再加一个∑j=1i−1f(pj)
所以
g(n,i)=⎩⎪⎨⎪⎧g(n,i−1)g(n,i−1)−f(pi)×[g(pin,i−1)−∑j=1i−1f(pj)]g(n,i)=nn<pi2pi2≤ni=0
由于最后实际要求的是g(n,∣P∣),我们只需要开一维就可以了,第二维可以类似滚动数组滚掉
又由于大于n的质因子只有一个
所以我们可以记f1[i]表示g(i,j),f2[i]表示g(in,j)
这样空间复杂度只有O(n)
代码:当f(p)=1即求素数个数时
代码实现比较精细
可以证明这样复杂度是O(lognn43)
int lim=sqrt(n);
for(int i=1;i<=lim;i++)f1[i]=i-1,f2[i]=n/i-1;
for(int p=2;p<=lim;p++){
if(f1[p]==f1[p-1])continue;
for(int i=1;i<=lim/p;i++)f2[i]-=f2[i*p]-f1[p-1];
for(int i=lim/p+1;1ll*i*p*p<=n&&i<=lim;i++)f2[i]-=f1[n/i/p]-f1[p-1];
for(int i=lim;i>=1ll*p*p;i--)f1[i]-=f1[i/p]-f1[p-1];
}
return f2[1];
但是会跑的很慢
你找一照发现问题很简单
卡一下数组枚举和除法以及一些常用变量就会快5、6倍
lim=sqrt(n);
for(i=1;i<=lim;++i)f1[i]=i-1,f2[i]=n/i-1,inv[i]=1.0/i;
for(p=2;p<=lim;++p){
if(f1[p]==f1[p-1])continue;
x0=f1[p-1],w1=lim/p,w2=min(lim,(n/p/p)),dd=n/p;
for(i=1;i<=w1;++i)f2[i]+=x0-f2[i*p];
for(i=lim/p+1;i<=w2;++i)f2[i]+=x0-f1[(int)(inv[i]*dd+1e-7)];
for(i=lim;i>=1ll*p*p;--i)f1[i]+=x0-f1[(int)(inv[p]*i+1e-7)];
}
return f2[1];
S
现在我们已经求出了∑i=1n[i∈P]f(i)
考虑如何计算合数的答案
设S(n,i)=∑j=1n[Minj≥pri]f(j)
即满足所有质因子大于等于pi的数的f之和
那么显然∑i=1nf(i)=S(n,1)+f(1)
考虑如何类似的递推
首先素数的答案已经算出来了
显然的是g(n,∣P∣)−∑j=1i−1f(prj)(因为S要求Minj≥pri)
考虑枚举合数的最小质因子和次数,直接算就是了
S(n,i)=g(n,∣P∣)−j=1∑i−1f(prj)+j=i∑prj2≤nxs=1∑prjxs+1≤nS(prjxsn,j+1)×f(prjxs)+f(prjxs+1)
好像这样的复杂度是lognn1−ϵ
不过跑的还比较快
然后就没了
代码看例题里的就可以了
例题:
LOJ6235
LOJ6202
LOJ6053
UOJ188
BZOJ5244
SPOJ−DIVCNTK