在非线形时间内筛某些奇性函数前缀和
狄利克雷卷积
(f∗g)(n)=d∣n∑f(d)g(dn)
一些结论,会用到( 注: I(n)=1,id(n)=n,ϵ(n)=[n=1] )
φ∗I=idμ∗I=ϵμ∗id=φ
线性筛 φ,μ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const int N = 100000; int p[N],tot; ll phi[N],mu[N]; bool v[N]; void init(int n) { for(int i=2;i<=n;++i) { if(!v[i]) p[++tot]=i,phi[i]=i-1,mu[i]=-1; for(int j=1;j<=tot&&p[j]*i<=n;++j) { v[p[j]*i]=1; if(i%p[j]==0) { mu[i*p[j]]=0; phi[p[j]*i]=phi[i]*p[j]; break; } mu[p[j]*i]=-mu[i]; phi[p[j]*i]=phi[i]*(p[j]-1); } } }
|
杜教筛
现在我们要求 f 的前缀和,记 F(n)=i=1∑nf(i) ,我们再找一个积性函数 g ,(g 要满足什么性质待会儿再说)
考虑展开 ∑i=1n(f∗g) :
i=1∑n(f∗g)=i=1∑nd∣i∑f(d)g(di)=d=1∑ng(d)i=1∑⌊n/d⌋f(i)=d=1∑ng(d)F(⌊n/d⌋)根据狄利克雷卷积更改求和顺序根据定义
再考虑 g(1)F(n)
g(1)F(n)=d=1∑ng(d)F(⌊n/d⌋)−d=2∑ng(d)F(⌊n/d⌋)=i=1∑n(f∗g)−d=2∑ng(d)F(⌊n/d⌋)两前缀和向减根据之前求的直接代
这就完事了,只要能快速求出 g 的前缀和,和 f∗g 的前缀和,式子右边数论分块再递归求解即可,可以线性筛预处理一部分,同时加上记忆化搜索。
伪代码
1 2 3 4 5 6 7 8 9 10
| ll getF(int n) { if(n < N) return preF[n]; if(memF[n]) return memF[n]; ll ret = sum_fg(n); for (int i = 2, j; i <= n; i = j + 1) { j = n / ( n / i ); ret -= getF( n / i ) * (sum_g(j) - sum_g( i - 1 ) ); } return memF[n] = ret; }
|
筛欧拉函数
注意到 φ∗I=id ,I(n)=1,id(n)=n ,这两前缀和有手就行,直接代公式,得到:
F(n)=2n(n+1)−d=2∑nF(⌊n/d⌋)
1 2 3 4 5 6 7 8 9 10
| ll PHI(ll x) { if(x<N) return phi[x]; if(phi_b[x]) return phi_b[x]; ll ret=(1ll+x)*x/2ll; for(ll i=2ll,j;i<=x;i=j+1) { j=x/(x/i); ret-=PHI(x/i)*(j-i+1ll); } return phi_b[x]=ret; }
|
筛莫比乌斯函数
根据 μ∗I=ϵ ,I(n)=1,ϵ(n)=[n=1] ,这前缀和一样有手就行。。
F(n)=1−d=2∑nF(⌊n/d⌋)
1 2 3 4 5 6 7 8 9 10
| ll MU(ll x) { if(x<N) return mu[x]; if(mu_b[x]) return mu_b[x]; ll ret=1ll; for(ll i=2ll,j;i<=x;i=j+1) { j=x/(x/i); ret-=MU(x/i)*(j-i+1ll); } return mu_b[x]=ret; }
|