杜教筛学习笔记

前言

头都给队友们打烂了啊
这玩意还是简单易懂的啊qwq
似乎博客已经变成了笔记博客??

用途

求一类积性函数的前缀和
经典问题有求 μ \mu μ ϕ \phi ϕ的前缀和,本文将以这两个函数的前缀和为例

前置

狄利克雷卷积
两个函数 f f f g g g的狄利克雷卷积 f ∗ g f*g fg即为
( f ∗ g ) ( i ) = ∑ d ∣ i f ( i ) g ( i d ) (f*g)(i)=\sum_{d|i}f(i)g(\frac{i}{d}) (fg)(i)=dif(i)g(di)

玩法

假设当前我们需要求 f ( x ) f(x) f(x)的前缀和 S ( x ) S(x) S(x)
宇宙惯例,一般 n n n很大求不出来所以我们得做一些奇妙的操作
假设我们能弄出来一个简单的积性函数 g ( x ) g(x) g(x)
对其做狄利克雷卷积得到函数 f ∗ g f*g fg
( f ∗ g ) ( i ) = ∑ d ∣ i f ( i ) g ( i d ) (f*g)(i)=\sum_{d|i}f(i)g(\frac{i}{d}) (fg)(i)=dif(i)g(di)

不妨设 ( f ∗ g ) ( x ) (f*g)(x) (fg)(x)的前缀和函数为 p r e ( x ) pre(x) pre(x),则有
p r e ( x ) = ∑ i = 1 x ∑ d ∣ i f ( i ) g ( i d ) pre(x)=\sum_{i=1}^x\sum_{d|i}f(i)g(\frac{i}{d}) pre(x)=i=1xdif(i)g(di)
把后面那个项提出来可以得到
p r e ( x ) = ∑ d = 1 x g ( d ) ∗ ∑ d ∣ i f ( i d ) pre(x)=\sum_{d=1}^{x}g(d)*\sum_{d|i}f(\frac{i}{d}) pre(x)=d=1xg(d)dif(di)
p r e ( x ) = ∑ d = 1 x g ( d ) ∗ ∑ i = 1 ⌊ i d ⌋ f ( i ) pre(x)=\sum_{d=1}^{x}g(d)*\sum_{i=1}^{\lfloor\frac{i}{d}\rfloor}f(i) pre(x)=d=1xg(d)i=1dif(i)
p r e ( x ) = ∑ d = 1 x g ( d ) ∗ S ( ⌊ i d ⌋ ) pre(x)=\sum_{d=1}^{x}g(d)*S(\lfloor\frac{i}{d}\rfloor) pre(x)=d=1xg(d)S(di)

我们不难得到下面的一个式子
g ( 1 ) S ( n ) = ∑ i = 1 n g ( i ) ∗ S ( ⌊ n i ⌋ ) − ∑ i = 2 n g ( i ) ∗ S ( ⌊ n i ⌋ ) g(1)S(n)=\sum_{i=1}^{n}g(i)*S(\lfloor\frac{n}{i}\rfloor)-\sum_{i=2}^{n}g(i)*S(\lfloor\frac{n}{i}\rfloor) g(1)S(n)=i=1ng(i)S(in)i=2ng(i)S(in)
注意到 S ( i ) = ∑ j = 1 i f ( j ) S(i)=\sum_{j=1}^{i}f(j) S(i)=j=1if(j),考虑在狄利克雷卷积中,每个 g ( d ) g(d) g(d)相乘的 f ( i d ) f(\frac{i}{d}) f(di) i d \frac{i}{d} di的值域在 [ 1 , n i ] [1,\frac{n}{i}] [1,in]中,故不难得到其实前面是一个狄利克雷卷积和
那么式子可以变为
g ( 1 ) S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) ∗ S ( ⌊ n i ⌋ ) g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^{n}g(i)*S(\lfloor\frac{n}{i}\rfloor) g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(in)
如果狄利克雷卷积的前缀和可以快速求出,且 g g g的前缀和也能快速求出
那么就可以对后面的 S ( i ) S(i) S(i)数论分块,记忆化一发开始gank
如果能预处理前 n 2 3 n^{\frac{2}{3}} n32的前缀和就能做到 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)

μ \mu μ

首先能知道一个结论就是
∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
那么我们构造 g ( x ) = 1 g(x)=1 g(x)=1
显然狄利克雷卷积的任意前缀和均为 1 1 1
那么就是要算
S ( n ) = 1 − ∑ i = 2 n g ( i ) ∗ S ( n i ) S(n)=1-\sum_{i=2}^{n}g(i)*S(\frac{n}{i}) S(n)=1i=2ng(i)S(in)
那么直接做就好了

ϕ \phi ϕ

又出来一个结论就是
∑ d ∣ n ϕ ( d ) = n \sum_{d|n}\phi(d)=n dnϕ(d)=n
证明不妨考虑枚举 [ 1 , n ] [1,n] [1,n]中的数与 n n n g c d gcd gcd
同样构造 g ( x ) = 1 g(x)=1 g(x)=1
显然狄利克雷卷积的某个前缀和 i i i即为 i ∗ ( i + 1 ) 2 \frac{i*(i+1)}{2} 2i(i+1)
那么就是要算
S ( n ) = ( 1 + n ) ∗ n 2 − ∑ i = 2 n g ( i ) ∗ S ( n i ) S(n)=\frac{(1+n)*n}{2}-\sum_{i=2}^{n}g(i)*S(\frac{n}{i}) S(n)=2(1+n)ni=2ng(i)S(in)
同样直接做就好了

模板

bzoj3944 sum
即为筛 ϕ \phi ϕ μ \mu μ的前缀和
注意 2 31 − 1 2^{31}-1 2311加一可能会爆 i n t int int

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair
#define pii pair
using namespace std;
inline int read()
{
	int f=1,x=0;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;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(LL x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=5000005;
const int MAXM=50005;
int n,N;
int pr[MAXN],plen,mu[MAXN],phi[MAXN];
bool is[MAXN];
LL pre1[MAXN],pre2[MAXN];
void init()
{
	phi[1]=1;mu[1]=1;
	for(int i=2;i<MAXN;i++)
	{
		if(!is[i])pr[++plen]=i,mu[i]=-1,phi[i]=i-1;
		for(int j=1;j<=plen&&i*pr[j]<MAXN;j++)
		{
			is[i*pr[j]]=true;
			if(!(i%pr[j]))
			{
				mu[i*pr[j]]=0;phi[i*pr[j]]=phi[i]*pr[j];
				break;
			}
			else mu[i*pr[j]]=-mu[i],phi[i*pr[j]]=phi[i]*(pr[j]-1);
		}
	}
	for(int i=1;i<MAXN;i++)pre1[i]=pre1[i-1]+mu[i],pre2[i]=pre2[i-1]+phi[i];
}
bool vi1[2*MAXM],vi2[2*MAXM];
LL id1[2*MAXM];
int getid(int x){return x<=N?x:n/x+N;}
LL solvemu(int x)
{
	if(x<MAXN)return pre1[x];
	int u=getid(x);
	if(vi1[u])return id1[u];
	LL ret=1;vi1[u]=true;
	for(int i=2,nxt;i<=x;i=nxt+1)
	{
		nxt=x/(x/i);
		LL s=solvemu(x/i);
		ret-=1LL*(nxt-i+1)*s;
		if(nxt==x)break;
	}
	return id1[u]=ret;
}
LL id2[2*MAXM];
LL solvephi(LL x)
{
	if(x<MAXN)return pre2[x];
	int u=getid(x);
	if(vi2[u])return id2[u];
	LL ret=1LL*(x+1)*x/2;vi2[u]=true;
	for(int i=2,nxt;i<=x;i=nxt+1)
	{
		nxt=x/(x/i);
		LL s=solvephi(x/i);
		ret-=1LL*(nxt-i+1)*s;
		if(nxt==x)break;
	}
	return id2[u]=ret;
}
int main()
{
	init();
	int T=read();while(T--)
	{
		n=read();N=sqrt(n);
		if(n<MAXN){pr1(pre2[n]),pr2(pre1[n]);continue;}
		memset(vi1,false,sizeof(vi1));memset(vi2,false,sizeof(vi2));
		pr1(solvephi(n));
		pr2(solvemu(n));
	}
	return 0;
}

你可能感兴趣的:(学习笔记啥的)