把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

杜教筛学习笔记

杜教筛是个奇奇怪怪的东西。在此之前先介绍一个更奇奇怪怪的东西

狄利克雷卷积

定义\((f*g)(n)=\sum\limits_{d|n}{f(d)\times g(\frac{n}{d})}\)
那么有几个基本结论:
\(\mu*I=\varepsilon\)
\(\mu*id=\phi\)
\(\phi*I=id\)

杜教筛

杜教筛是一个求积性函数前缀和的东西。
如果我们想求一个积性函数\(f\)的前缀和,设法找到一个\(g\)卷上它。再求前缀和
\(S(n)=\sum\limits_{i=1}^{n}{f_i}\)
那么\(\sum\limits_{i=1}^{n}{(f*g)(i)}=\sum\limits_{i=1}^{n}{\sum\limits_{d|i}{f(d)\times g(\frac{i}{d})}}\)
\(=\sum\limits_{d=1}^{n}{g(d)\times S(\lfloor\frac{n}{d}\rfloor)}\)
如果我们对其进行微小的扰动就变成这样\(S(n)g(1)=\sum\limits_{d=1}^{n}{g(d)\times S(\lfloor\frac{n}{d}\rfloor)}-\sum\limits_{d=2}^{n}{g(d)\times S(\lfloor\frac{n}{d}\rfloor)}\)
我们想前面那个如果能直接算那么剩下的递归求解即可。
这时候我们就要找到一个好的积性函数卷上去然后后面的整除分块计算即可。
时间复杂度是两次整除分块的\(O(n^{\frac{3}{4}})\),可能你还需要一个map来记录之类的。
我们发现这个复杂度其实很劣,再乘上一个map基本等于\(O(n)\)。所以我们要有一个优雅一点的算法来处理这个东西。
其实当求的\(n\)很大时递归下去要求的也很大的\(n\)其实很少,而剩下的都很小,这就可以线性筛预处理一部分答案。
如果我们取\(m=n^{\frac{2}{3}}\)理论时间复杂度最优是\(O(n^{\frac{2}{3}})\),而且因为一大部分直接查询了所以map的查询次数会少很多。
来看一道题目P4213 【模板】杜教筛(Sum)
题目中要求两个函数:\(\mu\)\(\phi\)
那么都卷上\(I\)即可。
code:

#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 5000000
#define eps (1e-14)
#define mod 998244353
#define U unsigned
using namespace std;
ll n,mu[N+5],m=1<<22,phi[N+5];int T,fl[N+5],pr[N+5],ph;map<int,ll> g1,g2;
I ll Getmu(ll x){
	if(x<=m) return mu[x];if(g1[x]) return g1[x];ll ans=1;re U int i,j;
	for(i=2;i<=x;i=j+1) j=x/(x/i),ans-=Getmu(x/i)*(j-i+1);return g1[x]=ans;
}
I ll Getphi(ll x){
	if(x<=m) return phi[x];if(g2[x]) return  g2[x];ll ans=(ll)x*(x+1)/2;re U int i,j;
	for(i=2;i<=x;i=j+1) j=x/(x/i),ans-=Getphi(x/i)*(j-i+1);return g2[x]=ans;
}
int main(){
//	freopen("1.in","r",stdin);
	re int i,j;scanf("%d",&T);mu[1]=1;phi[1]=1;
	for(i=2;i<=m;i++){
		if(!fl[i]) pr[++ph]=i,mu[i]=-1,phi[i]=i-1;
		for(j=1;j<=ph&&i*pr[j]<=m;j++){
			fl[i*pr[j]]=1;if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
			mu[i*pr[j]]=-mu[i];phi[i*pr[j]]=phi[i]*phi[pr[j]];
		}
	}
	for(i=2;i<=m;i++) mu[i]+=mu[i-1],phi[i]+=phi[i-1];
	while(T--){
		scanf("%lld",&n);printf("%lld %lld\n",Getphi(n),Getmu(n));
	}
}
posted @ 2021-06-03 19:06  275307894a  阅读(50)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end