Min25筛
Posted chasedeath
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Min25筛相关的知识,希望对你有一定的参考价值。
Min25筛
文章很多的向下取整省略了。。。
前言
为什么网上通行的写法和论文里不一样啊
问题引入
设质数集合为(p_i),(n)包含的质因数集为(p(n)),定义积性函数
其中,(G(p_i))为简单多项式函数(复杂了不好搞啊)
我们要求(S(n)=sum _1^n F(i))
设(m=lfloor sqrt{n} floor),我们通过对于(p_i>m)的因数只会最多出现一次的性质来简化这个问题
接下来的计算过程分为两部分
Part1.
对于每一个(xin[1,n])计算(sum _{p_ileq frac{n}{x}} G(p_i)),可以看到这样的(frac{n}{x})个数是(O(m))的
由于前面提到(G(p_j))是简单的多项式,所以可以拆出(G(p_j))的每一项(i^k)分别计算贡献
考虑对于(p_jleq m)的质数依次筛,定义(g_k(j,n))为考虑前(j)个质数,(sum _{1<ileq n,i in p or p(i)_{min}>p_j}i^k)
即与(kin[1,j], p_k)都互质或者是本身是质数的(i^k)之和,注意这个状态是不包括(1)的
边界条件(g_k(0,n)=sum _1^ni^k),这个拉格朗日插值法求,咕了
递推式(g_k(j,n)=g_k(j-1,n)-p_j^k (g_k(j-1,frac{n}{p_j})-g_k(j-1,p_{j-1})))
即每次减去不合法的,把质数的部分取回来
显然的性质有:
(nleq p_j ightarrow g_k(j,n)=sum_{p_ileq n}p_i^k)
(n< p_j^2 ightarrow g_k(j,n)=g_k(j-1,n)-p_j^k (g_k(j-1,frac{n}{p_j})-g_k(j-1,p_{j-1}))=g_k(j-1,n))
所以(n< p_j^2)的部分可以直接跳过
递推方法:
由于(lfloor frac{n}{ab}
floor=lfloor frac{lfloorfrac{n}{a}
floor}{b}
floor)(证明自行意会吧..)
可以看到每次的(n‘)都是原先的(frac{n}{d}),这样的数只有(O(m))个,即第二维状态只有(O(m))个,处理出这些状态然后进行递推
下面是以(G(p)=p)为例的递推
变量名解释:
(s_n=sum_1^n p_i^k ,pri[i]=p_i,st[i])存储第(i)个状态,(cnt)是状态个数
(g[n]=g_1(j,n)),由于递推过程中滚动省去了第一维
const int N=1e5+10;
int pri[N],notpri[N];
int id(int x){ return x<=m?x:cnt-n/x+1; } // O(1)访问状态编号
int st[N],cnt;
ll g[N],s[N];
int main(){
rep(i,2,N-1) if(!notpri[i]) {// 预先筛出素数,实际上并不必须
pri[++pc]=i;
for(int j=i+i;j<N;j+=i) notpri[j]=1;
}
n=rd(),m=sqrt(n);
cnt=0;
rep(i,1,n) st[++cnt]=i=n/(n/i),g[cnt]=1ll*i*(i+1)/2-1; // 预处理状态和初始值,不包括1
for(sz=1;pri[sz+1]<=m;++sz);
rep(i,1,sz) s[i]=s[i-1]+pri[i];
for(int i=1;i<=sz;++i) {
for(int j=cnt,tmp=pri[i]*pri[i];st[j]>=tmp;--j) {
int k=id(st[j]/pri[i]);
g[j]-=pri[i]*(g[k]-s[i-1]); // 取回前面的质数
}
}
}
由于式子比较麻烦,求解复杂度的部分就咕了
Part2.
这次要求出答案(S(n)=sum F(i))
定义(f(j,n)=sum _{p(i)_{min}ge p_j}F(i)),求出(f(1,n))后加上(F(1))即可
(f(j,n))的求解分为两部分
质数部分:(sum _{ige j} G(p_i)),这个可以通过访问先前求出的(g(j,n))来得到
合数部分:枚举包含的最小质因数为(p_i(ige j,p_i<n))
(f(j,n)leftarrowsum_{c>1,p_i^cleq n} F(p_i^c)+sum F(p_i^c)cdot f(i+1,frac{n}{p_i^c}))
剪枝
1.(n<p_j ightarrow f(j,n)=0)
2.不需要转移(p_i^2>n)的部分
这一部分由于只有一次查询,所以可以直接用递归来实现
以下是(G(p)=p)的例子
变量名:都和上面是一样的!
ll GetF(int j,int n){
if(n<pri[j]) return 0;
ll ans=g[id(n)]-s[j-1];// 质数的部分,注意不包含前面的j-1个质数
for(int i=j;1ll*pri[i]*pri[i]<=n;++i) {
int x=pri[i];
while(1ll*x*pri[i]<=n){
ans+=GetF(j+1,n/x)*x;
x*=pri[i];
ans+=x;// 转移,两种写在一起了
}
}
return ans;
}
复杂度又咕了
以上是关于Min25筛的主要内容,如果未能解决你的问题,请参考以下文章