据CCH和LJH说,杜教筛似乎是一个非常套路的东西,几乎所有的杜教筛的题目推理方式都是一模一样的(但实测有些推理还是很恶心的)。所以复习杜教筛不需要太多时间,粗略看一遍,留下印象即可。
杜教筛其实是一种简化运算的推理方式,它的使用条件并不仅限于积性函数(?),只是积性函数可以将复杂度进一步优化。
因为杜教筛是一种推理方式,所以直接给出例题反而容易上手。
欧拉函数的定义这里就不再赘述。
需要知道的是欧拉函数 (φ) ( φ ) 的一个特殊性质:
下面开始推导:
这就是一个很经典的杜教筛的题目。
模板题
#include
#include
#include
#include
#include
#define MAXN 500010
#define MAXP 500010
#define MOD 1000000007
#define SF scanf
#define PF printf
using namespace std;
bool isprime[MAXN];
int primes[MAXN],cnt;
long long fi[MAXN];
vectorlong long,long long> > ans[MAXP+10];
void prepare(){
fi[1]=1;
for(int i=2;i<=MAXN-10;i++){
fi[i]%=MOD;
if(isprime[i]==0){
primes[++cnt]=i;
fi[i]=i-1;
}
for(int j=1;j<=cnt&&i*primes[j]<=MAXN-10;j++){
isprime[i*primes[j]]=1;
if(i%primes[j]==0){
fi[i*primes[j]]=fi[i]*primes[j];
break;
}
fi[i*primes[j]]=fi[i]*fi[primes[j]];
}
}
for(int i=2;i<=MAXN-10;i++)
fi[i]=(fi[i-1]+fi[i])%MOD;
}
long long get_num(long long n){
long long px=n%MAXP;
for(int i=0;iif(ans[px][i].first==n)
return ans[px][i].second;
return -1;
}
void push_num(long long n,long long res){
long long px=n%MAXP;
ans[px].push_back(make_pair(n,res));
}
long long sum(long long n){
if(n<=MAXN-10)
return fi[n];
long long res=get_num(n);
if(res>=0)
return res;
if(n%2==0)
res=(((n/2)%MOD)*((n+1ll)%MOD))%MOD;
else
res=((n%MOD)*(((n+1ll)/2)%MOD))%MOD;
for(long long p=2;p<=n;){
long long len=n/(n/p)+1ll-p;
res=(res-len*sum(n/p))%MOD;
if(res<0)
res+=MOD;
p+=len;
}
push_num(n,res);
return res;
}
int main(){
prepare();
long long n;
/*for(int i=1;i<=10000;i++)
if(fi[i]!=sum(i)){
PF("Error!%d: %d %d\n",i,fi[i],sum(i));
break;
}*/
SF("%lld",&n);
PF("%lld",sum(n));
}
我们类比欧拉函数前缀和的取值方式,其实莫比乌斯函数也有一个类似的性质:
最后的最后,也是将前 n23 n 2 3 线性筛求出,复杂度也为 O(n23) O ( n 2 3 )
套路基本就是这些,有些恶心题会在此基础上套一些变换,使得杜教筛看起来不那么板。那些就基本上只能看数(ren)学(pin)能力和经验了。
模板题
#include
#include
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 5000010
#define MAXP 2181271
using namespace std;
bool isprime[MAXN],found;
int primes[MAXN],cnt,mu[MAXN];
map<long long,int> ans;
/*vector >ans[MAXP+10];
long long get_num(long long x){
found=1;
long long px=x%MAXP;
for(int i=0;i
void prepare(){
mu[1]=1;
for(int i=2;i<=MAXN-10;i++){
if(isprime[i]==0){
mu[i]=-1;
primes[++cnt]=i;
}
for(int j=1;j<=cnt&&primes[j]*i<=MAXN-10;j++){
isprime[i*primes[j]]=1;
if(i%primes[j]==0){
mu[i*primes[j]]=0;
break;
}
mu[i*primes[j]]=-mu[i];
}
}
for(int i=1;i<=MAXN-10;i++)
mu[i]=mu[i-1]+mu[i];
}
long long l,r;
long long sum(long long n){
if(n<=MAXN-10)
return mu[n];
if(ans.count(n)!=0)
return ans[n];
/*long long res=get_num(n);
if(found==1)
return res;*/
long long res=1ll;
for(long long q=2;q<=n;){
long long len=n/(n/q)+1ll-q;
res-=sum(n/q)*len;
q+=len;
}
ans[n]=res;
//push_num(n,res);
return res;
}
int main(){
prepare();
SF("%lld%lld",&l,&r);
PF("%lld",sum(r)-sum(l-1));
}