题解:P4213 【模板】杜教筛
前置知识
积性函数
顾名思义,积性函数是一类满足
线性筛
可以用 既然都来学习杜教筛了,一定会线性筛了吧。
代码如下:
const int N=1e7;
bool pri[N];
int prime[N],mul[N],phi[N],cnt[N],idx;
inline void sieve(int x){
mul[1]=1;
phi[1]=1;
memset(pri,true,sizeof pri);
for(int i=2;i<=x;i++){
if(pri[i]) prime[++idx]=i,mul[i]=-1,phi[i]=i-1,cnt[i]=1;
for(int j=1;j<=idx&&prime[j]*i<=x;j++){
pri[i*prime[j]]=false;
if(i%prime[j]) mul[i*prime[j]]=-mul[i],phi[i*prime[j]]=phi[i]*(prime[j]-1),cnt[i*prime[j]]=cnt[i]+1;
else{mul[i*prime[j]]=0,phi[i*prime[j]]=phi[i]*prime[j],cnt[i*prime[j]]=cnt[i];break;}
}
}
}
狄利克雷卷积
存在算术函数
或
两种情况本质上是一致的,只是两种不同的写法。
这里给出几对常见的
-
\mu*I=\epsilon$ 源于 $\sum\limits_{d|n} \mu(d)=[n=1] -
\varphi*I=id$ 源于 $\sum\limits_{d|n}\varphi(d)=n -
\mu*id=\varphi$ 源于 $\varphi(n)=\sum\limits_{i=1}^n [gcd(i,n)=1]=\sum\limits_{d|n}\frac{n}{d}\mu(d) -
I*id=\sigma
其中
整除分块
对于
杜教筛
杜教筛是一种用于求数论函数前缀和的非线性算法,可以在
为了快速求
其中记
于是我们可以得到:
根据这个式子我们可以写出如下伪代码:
long long get_fsum(int x){
long long res=sumh(x);//h的前缀和
for(int i=2;i<=x;i=r+1){
int r=x/(x/i);//整除分块
res-=(sumg(r)-sumg(i-1))*get_fsum(x/i);
}
return res;
}
我们假设
设在
我们可以将
对于
已知
对于
分别带入上下界得:
于是总时间复杂度为:
我们还可以继续优化杜教筛,我们先用线性筛筛出
实际应用
接下来我们回归题目,对于求
CODE
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,pri[1000005],prime[1000005],idx,mul[1000005],phi[1000005];
unordered_map<int,int>mull,phii,book_mul,book_phi;
const int len=1000000;
void init(){
mul[1]=1;
phi[1]=1;
memset(pri,true,sizeof pri);
for(int i=2;i<=len;i++){
if(pri[i]) {prime[++idx]=i;mul[i]=-1;phi[i]=i-1;}
for(int j=1;j<=idx&&i*prime[j]<=len;j++){
pri[i*prime[j]]=false;
if(i%prime[j]){mul[i*prime[j]]=-mul[i];phi[i*prime[j]]=phi[i]*(prime[j]-1);}
else{mul[i*prime[j]]=0;phi[i*prime[j]]=phi[i]*prime[j];break;}
}
}
for(int i=1;i<=len;i++)mul[i]+=mul[i-1],phi[i]+=phi[i-1];
}
int get_mul(int x){
if(x<=len)return mul[x];
else if(book_mul[x]) return mull[x];
int r,res=1;
for(int i=2;i<=x;i=r+1){
r=x/(x/i);
res-=(r-i+1)*get_mul(x/i);
}
book_mul[x]=1,mull[x]=res;
return res;
}
int get_phi(int x){
if(x<=len)return phi[x];
else if(book_phi[x]) return phii[x];
int r,res=(x+1)*x/2;
for(int i=2;i<=x;i=r+1){
r=x/(x/i);
res-=(r-i+1)*get_phi(x/i);
}
book_phi[x]=1,phii[x]=res;
return res;
}
signed main(){
init();
cin>>t;
while(t--){
int n;
cin>>n;
cout<<get_phi(n)<<" "<<get_mul(n)<<endl;
}
return 0;
}