Min25筛
Min25筛可以在 的复杂度内求一个积性函数 的前缀和,要求 为关于 的简单多项式,且 可以快速计算。
我们对这个 的前缀和每一项按照素数和合数(和1)进行分类:
枚举每个合数的最小质因子及次数:
其中 代表 的最小质因子。
我们分成两步算上面这个式子。
1.求
记
为上文中“简单多项式”的一项(相当于把函数拆开来)。
当 时,最小的满足 的数为 ,不会有贡献。
否则,考虑从 转移到 。这个转移的过程中剔除了最小质因子为 且不满足条件的数的贡献,即:
关于 :
这是最小质因子恰为 的合数的贡献。这些合数可以表示为 ,其中 满足 。如何计算 ?
- 是所有素数或最小质因子大于 的数的 次方和。这恰好包含了所有 和所有小于 的素数(不需要这一部分,因为 乘后者的最小质因子是后者自身)。因此要减去后者的贡献,即 。(,因为没有小于 的合数的最小质因子大于 ,只包括了素数的贡献)。
所以我们有了 的递推式:
就是 ,,可以直接线性筛。
同时根据整除点的性质(),只需处理所有 位置的 。
在这一步之后,将 线性组合为 。
2.求后面一部分
记
则我们的答案就是 。
可以将上式中满足条件的数分为两个部分:
- 大于 的素数: (其中 是满足 的最大的 )
- 大于 的合数:枚举最小质因子,有
所以有
考虑直接爆搜。另外注意到上式不包含 ,直接加上即可。
例题:
求 。
不难发现 ()。
直接做即可。
::::info[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int unsigned long long//对 2^64取模
const int N=1e7+5;
int p[N],pcnt;
bool isp[N];
void init(int n){
// p[0]=1;
for(int i=2;i<=n;i++){
if(!isp[i]){
p[++pcnt]=i;
// cout<<i<<endl;
}
for(int j=1;j<=pcnt and i*p[j]<=n;j++){
isp[i*p[j]]=1;
if(i%p[j]==0)break;
}
}
}
int T,n,k,sqn;
int g[N];//g[i] g(第i个整除点,j)的值(其中j被压维)
int d[N],ptr;//整除点
int pos[3][N];//d[x]的索引位置,pos[1][x]是小的(n/i)的位置
//,pos[2][x]是大的(n/i)的位置(存的是 x=n/(n/i))
int S(int x,int y){
//S(n,j)
if(p[y]>x)return 0;
int posx;
if(x<=sqn)posx=pos[1][x];
else posx=pos[2][n/x];
int ans=g[posx]-y*(k+1);//y*(k+1)=g(p_y-1,y-1)
for(int i=y+1,tmp;i<=pcnt and p[i]*p[i]<=x;i++){
tmp=p[i];
for(int e=1;tmp<=x;e++,tmp*=p[i]){
//枚举p_i^e
ans+=(k*e+1)*S(x/tmp,i);
if(e>1)ans+=k*e+1;
}
}
return ans;
}
signed main(){
cin>>T;
while(T--){
pcnt=0;
cin>>n>>k;
sqn=sqrt(n);
init(sqn);ptr=0;
for(int i=1;i<=n;i=n/(n/i)+1){
d[++ptr]=n/i;
g[ptr]=n/i-1;//g(n/i,0)=n/i-1(不包含f(1))
if(n/i<=sqn)pos[1][n/i]=ptr;
else pos[2][n/(n/i)]=ptr;
}
for(int i=1;i<=pcnt;i++){
// cerr<<pcnt<<" "<<ptr<<" "<<p[i]<<" "<<d[1]<<endl;
for(int j=1;j<=ptr and p[i]*p[i]<=d[j];j++){
//计算g(d[j],i)
int itr;
if(d[j]/p[i]<=sqn)itr=pos[1][d[j]/p[i]];
else itr=pos[2][n/(d[j]/p[i])];
// cout<<i<<" "<<j<<" "<<itr<<endl;
g[j]-=g[itr]-(i-1);//f(p)=k+1,多项式和p无关(相当于0次项),不用乘p的幂
}
}
for(int i=1;i<=ptr;i++)g[i]*=(k+1);//f(p)=k+1,现在 g[x]=g(第x个整除点,n)=【<=第x个整除点的f(p)和】
// cerr<<g[1]<<" "<<g[2]<<" "<<g[3]<<" "<<g[4]<<endl;
cout<<S(n,0)+1<<endl;
}
return 0;
}
::::
。只需要第一部分。