min_25筛(Unfinished)
SX%N考了好几道min_25,但我还是没有学,因为听说是小概率考点,现在学也没时间了,考完再说吧。但被题解忽悠着订了今天的题,事后发现虽然他只字未提但这就是min_25板。
求 \(\sum_{p\le N,p\in\mathbb{P}}(N/p+N/p^2+...)(N\le 10^{11})\)。
题解:
拆成 \(\sum N/p\) 和 \(\sum (N/p^2+N/p^3+...)\) 两部分算,后者只需要算 \(p\le \sqrt N\),不在话下。
\(\sum N/p\) 用整除分块算,由于 \(N/p\) 有 \(O(\sqrt n)\) 种取值,我们考虑对每一个块的 \(r\),用 dp 求 \([1,r]\) 的质数个数。
设 \(f[i][n]\) 表示只考虑前 \(i\) 个质数的情况下,\([1,n]\) 中有几个“质数”。显然有:
其中 \(i\) 是 \(1\sim \sqrt n\) 的质数个数,为 \(O(\sqrt n)\) 的,\(n\) 只需枚举整除分块右端,一共 \(O(\sqrt n)\) 个,空间复杂度爆炸。考虑如果外层循环枚举 \(p\),每次的 \(n\) 只需要枚举满足 \(p^2\le n\) 的,可以预处理出所有右端点,则每次要取的只是一段均摊 \(O(\sqrt{\sqrt n})\) 的后缀。为什么呢?显然这样其实等价于交换循环顺序(即先枚举右端点 \(r\),再枚举 \(\le \sqrt r\) 的 \(p\)),这样显然就是 \(O(1+\sqrt 2+\sqrt3+...+\sqrt{\sqrt n}+...+\sqrt{\frac n 2}+\sqrt n)\),算出来 \(n=10^{11}\) 时大概是 4e8,实际常数较小,跑得很快。不过注意必须用前一种循环顺序,因为这样我们可以用类似滚动数组的方式转移,而不需要存两维 MLE。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=800000;
ll n,ans,tot,R[N],f[N],id1[N],id2[N];
int qn,n_p,p[N];
bool v[N];
inline int gid(ll x){return x<=qn?id1[x]:id2[n/x];}
int main(){
cin>>n,qn=sqrt(n);
v[1]=1;
for(int i=1;i<=qn;i++){
if(!v[i])p[++n_p]=i;
for(int j=1;j<=n_p&&p[j]<=qn/i;j++){
v[i*p[j]]=1;
if(i%p[j]==0)break;
}
}
for(int i=1;i<=n_p;i++)
for(ll j=1ll*p[i]*p[i];j<=n;j*=p[i])
ans+=n/j;
for(ll l=1,r;l<=n;l=r+1){
R[++tot]=r=n/(n/l);
if(r<=qn)id1[r]=tot;
else id2[n/r]=tot;
}
for(int i=1;i<=tot;i++)f[i]=R[i]-1;
for(int i=1;i<=n_p&&p[i]<=n/p[i];i++)
for(int j=tot;j&&1ll*p[i]*p[i]<=R[j];j--){
f[j]-=f[gid(R[j]/p[i])]-i+1;
}
for(int i=1;i<=tot;i++)ans+=(f[i]-f[i-1])*(n/R[i]);
cout<<ans;
}

浙公网安备 33010602011771号