话说有没有 min_25 大爷的介绍啊想看 qwq。
min_25 筛是一种由 min_25 开发的积性函数前缀和算法,时间复杂度为 \(O(\dfrac{n^{\frac 34}}{\log n})\)。
min_25 筛的适用范围有以下几个限定条件:
- 所求函数 \(f(x)\) 是一个积性函数。
- \(f(p^\alpha)\) 容易求解,其中 \(p\) 是质数,\(\alpha\) 是正整数。
- \(f(p)\) 容易表示为多个完全积性函数之和。
所以假如你看到一道题,对积性函数的描述是以 \(f(p^\alpha)\) 为基础的,那么它就很有可能是一道 min_25 筛板题。
min_25 筛是以发明人的用户名命名的,但是假如从算法思想的角度来说,它属于 \(dp\),也大量运用到了埃氏筛的思想。
第一步:\(g(n)=\sum\limits_{p\le n}f(p)\)
我们先单纯考虑 \(f(p)\) 为完全积性函数。如果不是的话,根据要求三,拆成多个积性函数分别求解后相加即可。
首先筛出 \(\le\sqrt n+1\) 的所有质数,更大的明显不太需要了,因为判断质数只需要把 \(\le\sqrt n\) 的质数全扫一遍就可以了。
\(g(n)\) 求解是困难的,考虑分解埃式筛的步骤。我们称通过了前 \(i\) 个质数的检验的数为 \(i\) 级质数 ,在集合 \(P_i\) 中。设
\(G(i,j)=\sum\limits_{k\in P_j,k\le i}f(k)\),那就有 \(g(i)=G(i,pr)\),其中第 \(pr\) 小的质数是最小的大于等于 \(\sqrt i\) 的质数。
考虑 \(dp\) 转移。设 \(p_i\) 表示第 \(i\) 小的质数,则有:
\[G(i,j)=G(i,j-1)-f(p_j)(G(\lfloor\frac i{p_j}\rfloor,j-1)-G(p_{j-1},j-1)) \]
显然建立数组的时候可以滚掉第二维,转移方程正确性可以结合埃式筛过程理解。
仔细观察,容易发现我们只需要维护第一维形如 \(\lfloor\frac nx\rfloor\) 的位置即可,至于所有用到的质数,都 \(\le\sqrt n\),所以包含在前一种情况中。离散化后简单 \(dp\) 即可。
时间复杂度分析有点难推,长这样:
\[\sum_{i\le\sqrt n}O(\pi(\sqrt i)+\pi(\sqrt\frac ni))=\sum_{i\le\sqrt n}O(\pi(\sqrt\frac ni)) \]
\[=\sum_{i\le\sqrt n}O(\frac{\sqrt\frac ni}{\log\sqrt\frac ni})=O(\int_1^\sqrt n\frac{\sqrt\frac nx}{\log\sqrt\frac nx}dx)=O(\frac{n^{\frac 34}}{\log n}) \]
第二步:\(s(n)=\sum\limits_{i\le n}f(i)\)
容易想到将质数和合数的贡献分开考虑。此时有两种思路:
第一种
设 \(S(i,j)=\sum\limits_{k\in([p_j,i]\cap P_j)}f(k)\),则有:
\[S(i,j)=g(i)-g(p_j)+\sum_{k=j+1}^{\pi(\sqrt i)}\sum_{e=1}^{\log_{p_k}i}f(p_k^e)(S(\lfloor\frac i{p_k^e}\rfloor)+[e\ne 1]) \]
这个式子的优势在于直接递归,连记忆化都不用就能 \(AC\)。
时间复杂度在 \(n\le 10^{13}\) 时为 \(O(\dfrac{n^{\frac 34}}{\log n})\),在更广泛的情况下大概是 \(O(n^{1-\epsilon})\) 的,其中 \(\epsilon\) 是一个无穷小量。
第二种
设 \(S(i,j)=\sum\limits_{k\in([1,i]\cap P_j)}f(k)\),则有:
\[S(i,j)=S(i,j+1)+[p_{j+1}\le\sqrt n]\sum_{e=1}^{\log_{p_{j+1}}i}f(p_{j+1}^e)(S(\lfloor\frac n{p_{j+1}^e}\rfloor,j+1)-g(\min\lfloor\frac n{p_{j+1}^e}\rfloor p_{j+1})+[e\ne 1]) \]
再进行简化:
\[S(i,j)=S(i,j+1)+[p_{j+1}\le\sqrt n]\sum_{e=1}^{-1+\log_{p_{j+1}}i}f(p_{j+1}^e)(S(\lfloor\frac n{p_{j+1}^e}\rfloor,j+1)-g(p_{j+1}))+f(p_{j+1}^{e+1}) \]
递推即可,时间复杂度 \(O(\dfrac{n^{\frac 34}}{\log n})\),但是大常数。不过好消息是我们可以顺带求出所有 \(\lfloor\dfrac ni\rfloor\)。
cpp
//first Luogu模版题代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5,p=1e9+7,is=(p+1)/6;
namespace min25{
int n,cnt,id1[N],id2[N],v[N];
int m,pr[N],vs[N],g1[N],g2[N];
int gt(int x){return x<N?id1[x]:id2[n/x];}
int s1(int x){return x*(x+1)/2%p;}
int s2(int x){return x*(x+1)%p*(x+x+1)%p*is%p;}
int f(int x){return x%=p,(x*x-x)%p;}
void init(){
for(int i=2;i<=sqrt(n)+1;i++){
if(vs[i]) continue;pr[++cnt]=i;
for(int j=i*2;j<=sqrt(n)+1;j+=i) vs[j]=1;
}for(int l=1,r;l<=n;l=r+1){
r=n/(n/l),v[++m]=n/l;
n/l<N?id1[n/l]=m:id2[r]=m;
g1[m]=(s1(n/l%p)-1)%p;
g2[m]=(s2(n/l%p)-1)%p;
}for(int j=1;j<=cnt;j++)
for(int i=1;i<=m&&pr[j]*pr[j]<=v[i];i++){
g1[i]=(g1[i]-pr[j]*(g1[gt(v[i]/pr[j])]-g1[gt(pr[j-1])]))%p;
g2[i]=(g2[i]-pr[j]*pr[j]%p*(g2[gt(v[i]/pr[j])]-g2[gt(pr[j-1])]))%p;
}
}int S(int x,int y){
if(pr[y]>=x) return 0;
int re=(g2[gt(x)]-g1[gt(x)]-g2[gt(pr[y])]+g1[gt(pr[y])])%p;
for(int i=y+1,w=pr[i];i<=cnt&&w*w<=x;w=pr[++i])
for(int j=1;w<=x/pr[i];j++,w*=pr[i])
re=(re+f(w)*S(x/w,i)%p+f(w*pr[i]))%p;
return re;
}int ans(int x){return init(),(S(x,0)+p+1)%p;}
}using namespace min25;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,cout<<ans(n);
return 0;
}
cpp
//second 简单的函数 10^13 版
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+5,p=1e9+7;
ll n,v[N];int m,sum[N],g[N],g1[N],g2[N];
int cnt,pr[N],vs[N],id1[N],id2[N],s[N];
int gt(ll x){return x<N?id1[x]:id2[n/x];}
int md(int x){return (x>=p)?x-p:(x<0)?x+p:x;}
int md(ll x){return x-x/p*p;}
void init(){
for(int i=2;i<=sqrt(n)+1;++i){
if(vs[i]) continue;
pr[++cnt]=i,sum[cnt]=md(sum[cnt-1]+i);
for(int j=i*2;j<=sqrt(n)+1;j+=i) vs[j]=1;
}for(ll l=1,r,k;l<=n;l=r+1){
v[++m]=n/l,r=n/v[m];
v[m]<N?id1[v[m]]=m:id2[r]=m;
g1[m]=(k=md(v[m]))-1;
g2[m]=md(k*(k+1)/2-1);
}for(int j=1,c=pr[j];j<=cnt;c=pr[++j])
for(int i=1;i<=m&&c<=v[i]/c;++i){
g1[i]=md(g1[i]-g1[gt(v[i]/c)]+j-1);
g2[i]=md((ll)g2[i]-(ll)c*(g2[gt(v[i]/c)]-sum[j-1]));
}
for(int i=1;i<=m;i++) s[i]=g[i]=md(g2[i]-g1[i]);
for(int j=cnt,c=pr[j];j;c=pr[--j])
for(int i=1;i<=m&&c<=v[i]/c;++i)
for(ll k=1,w=c;w<=v[i]/c;++k,w*=c)
s[i]=md((c^k)*(s[gt(v[i]/w)]-g[gt(c)])+(ll)s[i]+(ll)(c^(k+1)));
}int ans(){
int re=0;s[m]-=2;
for(int i=1;i<=m;++i) s[i]=md(s[i]+p+3);
sort(s+1,s+m+1),m=unique(s+1,s+m+1)-s-1;
for(int i=1;i<=m;++i) re^=s[i];
return re;
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,init(),cout<<ans();
return 0;
}