P5325-[模板]Min_25筛

1|0正题

题目链接:https://www.luogu.com.cn/problem/P5325


1|1题目大意

定义一个积性函数满足f(pk)=pk(pk1)
i=1nf(i)


1|2解题思路

首先我们可以把f(pk)是质数的情况拆成一个2阶的多项式f(x)=x2x

然后就是Min25筛了。我们先需要求出质数有关的答案,考虑把这个多项式的多个阶分开来求。

有关质数的求法,我们定义dp数组

gk(n,j)=i=1n[iPri  or  minp(i)>pj]ik

(上面Pri表示质数集,minp(x)表示x的最小质因子,pj表示n的第j个质因子,k表示多项式的某个阶,后面为了方便gk写作g,后文中的g(n,j)中的n都与输入的n无关,只是一个变量)

具体的说就是如果i是质数或者最小质因子超过pj,那当某个pj最接近但小于n时,g(n,j)就是我们需要的答案了。

因为一个合数x满足minp(x)x,所以pj的数量级很小,可以利用这个性质。

递推这个数组时对于每次j往上加相当于多加上了一些限制条件,也就是g(n,j)相对于g(n,j1)我们需要减去一些多余的答案。

这些多余的答案就是最小质因数是pj的数,这些数就是pjk( g(npj,j1)g(pj1,j1) )
g(npj,j1)g(pj1,j1) )表示枚举一个乘上pj之后不会超过n且最小质因子超过pj的数,这些数乘上pj后就是不重不漏的表示最小质因子是pj的数,因为y=xk是一个完全积性函数,所以可以直接乘上一个pjk

现在我们就有递推式

g(n,j)=g(n,j1)+pjk( g(npj,j1)g(pj1,j1) )

快速处理g(n,0)然后递推,可以注意到g(pj1,j1)就是前j1个质数的k次方和,因为pj数量级只有n所以可以直接预处理,因为后面还要用就定义spi=i=1nf(pi)

还有发现无论如何每个g(x,j)的取值都只与nx有关,所以我们对于g数组的每一个j都只有n个连续的范围内的同一取值。所以我们可以直接整除分块来大大缩减数量级的空间和时间。

给出一个x如何快速得到g(x,j)的储存地址?一个比较巧妙的方法是如果xn那么ind1x表示x的储存位置,否则用ind2nx表示x的储存地址。

然后j那个维度我们只需要用到j=cntcnt表示n以内的质数数量)值,所以我们直接滚动来递推就好了。

现在我们知道了所有的g(i,cnt),这道题的话具体的值就是g2(i,cnt)g1(i,cnt)就可以表示前缀质数f的函数和了。下面简写g(i)=g2(i,cnt)g1(i,cnt)

然后就是要求答案了,同样使用dp的技巧,设

S(n,j)=i=2n[minp(i)>pj]f(i)

然后对于S(n,j)的答案我们可以分为质数和非质数两部分,质数部分显然就是g(n)spn(不记得sp的去看前面定义)。
然后合数部分我们考虑递归下去处理就是k>jpkenf(pke)(S(npke,k)+[e1])pke是枚举质因子就不多说了,那个[e1]是因为pk已经作为质数被计算过了,但是后面的pke还没有。

现在就有S(n,j)的递推式了

S(n,j)=g(n)spn+k>jpkenf(pke)(S(npke,k)+[e1])

递归下去做就好了,说是不知道因为啥定理所以是不用记忆化的。

一个细节就是f(1)最好不要统计进去,最好加上就好了

据说时间复杂度是O(n34logn)的,反正这题我没开O2的话1e10只跑了七百多毫秒。


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const ll N=1e6+10,P=1e9+7; ll n,T,cnt,tot,pri[N],sp1[N],sp2[N],ind1[N],ind2[N],g1[N],g2[N],w[N]; bool v[N]; void init(ll n){ for(ll i=2;i<=n;i++){ if(!v[i]){ pri[++cnt]=i; sp1[cnt]=(sp1[cnt-1]+i)%P; sp2[cnt]=(sp2[cnt-1]+i*i)%P; } for(ll j=1;j<=cnt&&i*pri[j]<=n;j++){ v[i*pri[j]]=1; if(i%pri[j]==0)break; } } return; } ll S(ll x,ll y){ if(pri[y]>=x)return 0; ll pos=(x>T)?ind2[n/x]:ind1[x]; ll ans=g2[pos]-g1[pos]-(sp2[y]-sp1[y]); ans=(ans%P+P)%P; for(ll i=y+1;i<=cnt&&pri[i]*pri[i]<=x;i++){ for(ll j=1,p=pri[i];p<=x;j++,p=p*pri[i]){ ll w=p%P; (ans+=w*(w-1)%P*(S(x/p,i)+(j!=1))%P)%=P; } } return ans; } signed main() { scanf("%lld",&n); T=sqrt(n);init(T); ll inv2=(P+1)/2,inv6=(P+1)/6; for(ll l=1,r;l<=n;l=r+1){ ll x=n/l;r=n/(n/l); w[++tot]=n/l;x%=P; g1[tot]=x*(x+1)%P*inv2%P-1; g2[tot]=x*(x+1)%P*(2*x+1)%P*inv6%P-1; if(n/l<=T)ind1[n/l]=tot; else ind2[n/(n/l)]=tot; } for(ll i=1;i<=cnt;i++) for(ll j=1;j<=tot&&pri[i]*pri[i]<=w[j];j++){ ll k=(w[j]/pri[i]);k=(k>T)?ind2[n/k]:ind1[k]; (g1[j]+=P-pri[i]*(g1[k]-sp1[i-1])%P)%=P; (g2[j]+=P-pri[i]*pri[i]%P*(g2[k]-sp2[i-1])%P)%=P; } printf("%lld\n",(S(n,0)+1)%P); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14284015.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(75)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示