Min_25筛学习笔记
由于Min_25筛过于难学于是又来记笔记了
Min_25筛用于求解次数比较小的多项式积性函数的前缀和。
拿洛谷例题为例:
定义积性函数\(f(x)\),且\(f(p^k)=p^k(p^k-1)\),求\(\sum\limits_{i=1}^nf(i)\)
由于次数比较低我们分次数考虑,下面过程都是在同一次数下进行。
首先按照质数和非质数分类:
其中\(minp_i\)表示i的最小质因子。
先看第一部分。
设\(g_k(n,j)=\sum\limits_{i=1}^{n}[i\ is\ a\ prime \ or\ minp>p_j]i^k\)
注意\(i^k\)只在质数处和f取值相同,也就是\(i^k\)是我们构造的一个完全积性函数。
然后考虑\(g(n,i)\)从\(g(n,i-1)\)转移过来,有:
也就是去掉不符合条件的合数,这些合数肯定都是最小质因子等于\(p_i\)的,然后去掉g中包含的质数。
然后注意到后面一项中\(g(p_{i-1},i-1)\)就是前\(j-1\)个质数的k次方和,由于\(p_i<=\sqrt{n}\)所以可以线性筛预处理,设为\(sp_x\)。那么[1,n]中所有质数的k次方和其实就是\(g(n,x)\),\(p_x\)是最后一个小于等于\(\sqrt{n}\)的质数,记做\(g(n)\).
但是n还是太大了。但是我们又发现 \(\left\lfloor\frac{n}{x}\right\rfloor\)只有\(\sqrt{n}\)级别,可以离散化记录一下分别是谁然后求解,这样的话对每个处于此之内的根号以下的质数都有1次求解,复杂度就是\(\sum\limits_{\left\lfloor\frac{n}{x}\right\rfloor}\sum\limits_{i=1}^{\sqrt{ \left\lfloor\frac{n}{x}\right\rfloor}}[i\ is\ a\ prime]\)。
然后求解答案。设\(f(n,x)\)表示\([1,n]\)中最小质因子大于\(p_x\)的数的f之和,答案就是\(S(n,0)\)
把它分成两部分,一部分是大于\(p_x\)的质数,也就是\(g(n)-sp_x\),剩下的枚举最小质因子:
然后递归求解即可。
然后放个码:
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
typedef long long ll;typedef double db;
#define pf printf
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
inline ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
const int N=2e5+10,mod=1e9+7,inv3=(mod+1)/3;
int sq,sp1[N],sp2[N],g1[N],g2[N],prime[N],co,id1[N],id2[N];bool vis[N];
ll w[N],n;
inline void init(int n){
F(i,2,n){
if(!vis[i])prime[++co]=i,sp1[co]=sp1[co-1]+i,sp1[co]-=sp1[co]>=mod?mod:0,sp2[co]=(1ll*i*i+sp2[co-1])%mod;
F(j,1,co){
if(prime[j]*i>n)break;
vis[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
inline int S(ll x,int y){
if(prime[y]>=x)return 0;
int k=x<=sq?id1[x]:id2[n/x];
int ans=(0ll+g2[k]-g1[k]+mod+sp1[y]-sp2[y]+mod)%mod;
F(i,y+1,co){
if(1ll*prime[i]*prime[i]>x)break;
int e=1;
for(ll p=prime[i];p<=x;e++,p*=prime[i]){
int v=p%mod;
ans+=1ll*v*(v+mod-1)%mod*(S(x/p,i)+(e!=1))%mod;
ans-=ans>=mod?mod:0;
}
}return ans;
}
inline short main(){
n=read(),sq=sqrt(n),init(sq);
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l),w[++w[0]]=n/l,g1[w[0]]=w[w[0]]%mod,
g2[w[0]]=1ll*g1[w[0]]*(g1[w[0]]+1)/2%mod*(2ll*g1[w[0]]+1)%mod*inv3%mod-1;
g1[w[0]]=1ll*g1[w[0]]*(g1[w[0]]+1)/2%mod-1;
if(n/l<=sq)id1[n/l]=w[0];else id2[r]=w[0];
}
F(i,1,co){
F(j,1,w[0]){
if(1ll*prime[i]*prime[i]>w[j])break;
int k=w[j]/prime[i]<=sq?id1[w[j]/prime[i]]:id2[n/(w[j]/prime[i])];
g1[j]-=1ll*prime[i]*(g1[k]-sp1[i-1]+mod)%mod;
g2[j]-=1ll*prime[i]*prime[i]%mod*(g2[k]-sp2[i-1]+mod)%mod;
g1[j]+=g1[j]<0?mod:0,g2[j]+=g2[j]<0?mod:0;
}
}pi(S(n,0)+1);
return 0;
}
}
signed main(){return EMT::main();}
然后下午刷了几道题
简单的函数
求出来每个质数的前缀和其实就好做了。发现\(f(i)\)当i不是2且i是质数时就是\(i-1\)于是分别求解\(f(i)=i\)和\(f(i)=1\),然后减一下,把2的值补上就完事了。
Sanrd
仔细观察可以发现我们无法发现积性函数。
但是我们还是可以套用Min_25筛的过程。设\(S(n,i)\)表示\(<=n\)的数中\(minp>p_i\)的f值之和,还发现质数和1没有贡献,于是直接就有:
就是枚举这个质数的贡献。其中\(g_i\)表示\(i\)以内质数个数,还是可以通过dp求出。
然后直接就是\(S(r,0)-S(l-1,0)\)
Misaka Network 与求和
简单化一下式子就变成了:
然后我们发现\((f*\mu)*1=f*(\mu*1)=f\)
于是设\(g(n)=\sum\limits_{i=1}^n(f*\mu)(i)\)
就有:
然后f可以min25筛然后杜教筛再整除分块一下即可。