@xiaoziyao
2021-07-07T11:11:35.000000Z
字数 2740
阅读 936
数学
学习笔记
线性筛以的复杂度完成了数论函数的值域内求值,可是若要以更低的复杂度解决前缀求值,就要考虑更优的筛法了,例如杜教筛,Powerful Number,可是这两个筛法对函数都有较高的要求,于是我们要掌握更普适的筛子——min25筛。
min25筛可以以(一说,最新版本好像是)质数幂次上值为一个多项式的积性函数的前缀和。
题意:给定一个积性函数,满足,求
定义为的最小质因子,为第个素数。
实际上,min25筛解决的问题可以解决是阶为常数的任意多项式的情况,本题只是一种特殊情况。
我们考虑对质数和合数分开考虑,后面的合数枚举其最小质因子及其幂次:
前面枚举到是因为任意合数的一定小于等于,后面那个是为了不算入而算入。
考虑对于每个单项式,设其幂次为,设函数在素数上拟合,即。
考虑怎么从转移过来,不难发现时,否则要减去的贡献,而这个贡献容斥一下就是(考虑提出质因子,然后减去中多算的素数),即:
根据上面的素数合数分开算的式子,设
本题Powerful Number写法是的,但由于常数原因,我的min25比我的Powerful Number快一倍以上。
#include<stdio.h>
#include<math.h>
const int maxt=100005,mod=1000000007,inv2=(mod+1)/2,inv6=(mod+1)/6;
int t,cnt,tot;
long long n;
int sum1[maxt],sum2[maxt],p[maxt],c[maxt],id[maxt][2],g1[maxt<<1],g2[maxt<<1];
long long val[maxt<<1];
inline int add(int a,int b){
return a+b>=mod? a+b-mod:a+b;
}
int S(long long v,int k){
if(p[k]>=v)
return 0;
int w=id[v<=t? v:(n/v)][v>t],res=add(add(g2[w],mod-sum2[k]),mod-add(g1[w],mod-sum1[k]));
for(int i=k+1;i<=cnt&&1ll*p[i]*p[i]<=v;i++)
for(long long j=1,mul=p[i];mul<=v;j++,mul*=p[i])
res=add(res,1ll*mul%mod*((mul-1)%mod)%mod*(S(v/mul,i)+(j>1))%mod);
return res;
}
int main(){
scanf("%lld",&n),t=sqrt(n),c[1]=1;
for(int i=2;i<=t;i++){
if(c[i]==0)
p[++cnt]=i,sum1[cnt]=add(sum1[cnt-1],i),sum2[cnt]=add(sum2[cnt-1],1ll*i*i%mod);
for(int j=1;j<=cnt&&i*p[j]<=t;j++){
c[i*p[j]]=1;
if(i%p[j]==0)
break;
}
}
for(long long L=1,R;L<=n;L=R+1){
long long v=n/L;
int w=v%mod;
R=n/v,val[++tot]=v,id[v<=t? v:R][v>t]=tot;
g1[tot]=add(1ll*w*(w+1)%mod*inv2%mod,mod-1),g2[tot]=add(1ll*w*(w+1)%mod*(w+w+1)%mod*inv6%mod,mod-1);
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<=tot&&1ll*p[i]*p[i]<=val[j];j++){
long long v=val[j]/p[i];
int w=id[v<=t? v:(n/v)][v>t];
g1[j]=add(g1[j],mod-1ll*p[i]*add(g1[w],mod-sum1[i-1])%mod);
g2[j]=add(g2[j],mod-1ll*p[i]*p[i]%mod*add(g2[w],mod-sum2[i-1])%mod);
}
printf("%d\n",add(S(n,0),1));
return 0;
}
SP34096 DIVCNTK - Counting Divisors (general)
参考资料:Min_25筛 学习小记