你别再飞走了……|

YYYmoon

园龄:1年粉丝:20关注:38

莫比乌斯反演、杜教筛、Min_25筛学习笔记

前置知识


一、整除分块

即按照 ni 的值域进行分块,块数 2n。分 ini>n 讨论。

i 所在块的右端点为 nni ,代码即 r=n/(n/l)

这里的证明:设所在块的值为 nl ,则这个块内的 ink ,把 k 带入即得。

这样,我们可以把 O(n) 的求 i=1nnif(i) 值过程变为 O(n) ( f(i)用前缀和处理 )

  • 无关的小知识

12+22+32+...+n2=n(n+1)(2n+1)6

13+23+33+...+n3=n2(n+1)24

另外,在做题的时候时刻关注的是式子的形式和范围,除数不为0,r不大于n

二、普通生成函数

形如 f(x)=nanxn,此处a可以有限也可以无限

如序列 a=1,3,5,7... (编号从0开始) 的生成函数是 n0(2n+1)xn ,如果a有通项公式,则它的普通生成函数的系数就是通项公式

基本运算

考虑 a,b 的普通生成函数 f(x),g(x)

加法,f(x)±g(x)=n(an±bn)xn ,即 f(x)±g(x) 是序列 qn=(an±bn) 的普通生成函数

乘法,或卷积,f(x)g(x)=nxni=0naibni,即 f(x)g(x) 是序列 qn=i=0naibni 的普通生成函数

关键在构造,一般指数是题中给的限制,某项系数是最终答案

可以解决多重集组合数

三、指数生成函数

形如 f(x)=n0anxnn!

这里,如果原数列是有限的,就把大于数列上界的 an 设为 0 即可

little question: 封闭形式如何得到?(坑)

加法同上

乘法(卷积),

f(x)g(x)=i0aixii!j0bjxjj!

=n0xni=0naibni1i!(ni)!

=n0xnn!i=0nn!i!(ni)!aibni

=n0xnn!i=0nCniaibni

f(x)g(x) 是序列 qn=i=0nCniaibni

可以解决多重集排列数

四、狄利克雷生成函数

形如 f(x)=n>0annx

乘法(卷积),

i>0aiixj>0bjjx

=(a11x+a22x+a33x+a44x+...)(b11x+b22x+b33x+b44x+...)

=a1b11x+a1b2+a2b12x+a1b3+a3b13x+a1b4+a2b2+a4b14x+...

=n>01nxd|nadbnd

五、积性函数

一个数论函数,f(1)=1,当gcd(a,b)=1时,有f(ab)=f(a)f(b),则f(n)为积性函数

ps.数论/算术函数指定义域为正整数、陪域为复数的函数(其实没啥用)

特别地,如果对于任意a,b(不一定互质),都有f(ab)=f(a)f(b),则f(n)为完全积性函数

欧拉函数和莫比乌斯函数都是积性函数

欧拉函数

φ(n)=i=1n[gcd(i,n)=1]

φ(n)=n(11p1)(11p2)...(11ps),其中p为n的质因数

即n以内,和n互质的数的个数

性质:d|nφ(d)=n

证明:将以n为分母,且比1小的非负分数写出来,化简后按分母归类,发现每一类 d 的个数都是 φ(d)

正主出场:壹、莫比乌斯函数

定义:

μ(n)={1n=1(1)sn=p1p2...ps0n包含相同质因子

可以用线性筛O(n)求出

性质: d|nμ(d)=[n=1]

简单证明:

n>1 时,n=p1a1p2a2..psas

n=p1p2...ps

d|nμ(d)=d|nμ(d)

约数由质因子的成绩构成,又有容斥原理

d|nμ(d)=Cs0+(1)Cs1+(1)2Cs2+...+(1)sCss

=(1+(1))s

=0

与欧拉函数的联系:d|nμ(d)nd=φ(n)

证明是把n提出来,因式分解,化成欧拉函数的形式。

性质应用:[gcd(i,j)=1]=d|gcd(i,j)μ(d)

常用手法:

1.YY的GCD 经过和式变换后整除的分母有两个变量,直接另T等于其分母,更改枚举顺序即可

2.[SDOI2015]约数个数和 设d(x)为x的约数个数和,有推论d(ij)=(ti+1)=(ai+bi+1)=x|iy|j[gcd(x,y)=1]

最后化成一个整除分块套整除分块的形式

莫比乌斯反演

形式一

f(n)=d|ng(d)

推出 g(n)=d|nf(nd)μ(d)

证明:令t(n)=d|nf(nd)μ(d)=d|nμ(d)i|ndg(i)=i|ng(i)d|niμ(d)

当i=n时,d|niμ(d)=1,则t(n)=g(n)

当i取小于n的约数时,d|niμ(d)=0,则t(i)=0

相加得证。

形式二

f(n)=n|dg(d)

推出 g(n)=n|dμ(dn)f(d)

又,有趣的事情在于,如果我们不学习莫比乌斯反演,只靠这之前的莫比乌斯函数性质+和式变换,大部分问题也是可以解决的。。。

(保守了,目前没见到无法解决的。。

总感觉每道莫反的题推法都十分类似。(当然我不是用莫反做的

for example: [国家集训队] Crash的数字表格 / JZPTAB

i=1nj=1mlcm(i,j)

=i=1nj=1mijgcd(i,j)

=i=1nj=1md=1nij1d[gcd(i,j)=d](单独枚举gcd

=d=1ni=1ndj=1mdijd[gcd(i,j)=1](更改枚举范围,得到gcd=1的形式)

=d=1ni=1ndj=1mdijdp=1nμ(p)[p|i][p|j](根据莫比乌斯函数推论)

=d=1ndp=1nμ(p)i=1npdj=1mpdp2ij(更改枚举范围+交换枚举顺序)

=d=1ndp=1nμ(p)p2(npd+1)npd2(mpd+1)mpd2(根据题目要求,推出可进行整除分块的部分)

=T=1n(nT+1)nT2(mT+1)mT2p=1nμ(p)p2Tp(分母有两个未知数,令T=分母,改为枚举T,以便把整除的部分分离出来)

=T=1n(nT+1)nT2(mT+1)mT2p=1nμ(p)pT(最后总会化成一个【整除分块*一个含μ的函数】的形式)

好的,不得不承认,确实有些题直接用莫比乌斯反演更简单。

如 luogu P6271 一个人的数论。

考虑构造 f(n)=i=1nik g(n)=i=1nik[gcd(i,n)=1] ,求 g(n)

f(n)=d|ndkg(nd) ,做莫比乌斯反演,有 g(n)=d|nμ(d)dkf(nd)

剩下是多项式转换内容。

贰、杜教筛

杜教筛可以在低于线性的时间复杂度内求出积性函数的前缀和。

常见的积性函数:μ,φ,σ(约数和),d(约数个数)

注,无论数论函数f是否为积性函数,只要可以构造出恰当的数论函数g,便都可以考虑用杜教筛求f的前缀和

再来放一下这张图

其中“三个常用函数”ϵ,I,id 均为完全积性函数

第三个"常用卷积关系"的推导:注意到μ φ的卷积关系中都有I,那么考虑把它们组合起来。

φI=id φIμ=idμ φϵ=idμφ=μid

算法思想:

考虑构造一个S(n)关于S(ni)的递推式。

对于任意一个数论函数g,必满足

i=1n(fg)(i)=i=1ndiig(d)f(id)=d=1ng(d)d|inf(id)(这一行被称为杜教筛变换)=d=1ng(d)i=1ndf(i)=d=1ng(d)S(nd)

其中 (fg) 为数论函数 fg 的 狄利克雷卷积。

那么可以得到(杜教筛公式):

g(1)S(n)=i=1ng(i)S(ni)i=2ng(i)S(ni)=i=1n(fg)(i)i=2ng(i)S(ni)

假如我们可以构造恰当的数论函数 g 使得,

可以快速计算 i=1nh(i)=i=1n(fg)(i)
可以快速计算 g 的前缀和,以用数论分块求解i=2ng(i)S(ni)

则我们可以在较短时间内求得 g(1)S(n)

Q:为什么我们要化成一个好求的函数h呢?直接求h的展开形式 i=1ng(i)S(ni) (它和 i=2ng(i)S(ni) 形式相同,只是范围不同)不行吗?

A:显然我们是由卷积的基本公式拆出来一个S(n),目的也是求解S(n). 如果此时我们再分别从 i=1ng(i)S(ni) 的形式去妄图O(n)求解,那程序就会在i=1时陷入求S(n)的循环中无法自拔。(应该不会有人和我一样唐吧?

写法:发现该公式为S(n)的递归形式,在递归时加上记忆化,让每个S(i)只被算一次。

时间复杂度不会证明,如果线性预处理k个S(n),时间复杂度会得到优化。其中k取n23时间复杂度最小值为O(n23).

在推 g(x) 的式子时,注意观察到类似
d|nf(d) 的样子,总之就是观察+猜测;又,后面一项是从2开始枚举的,不妨推式子的时候直接写成 d<n 最后更改枚举范围即可。

模板
//杜教筛模版
#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int k=2e6+5;
int T,n,vis[k],p[k],cnt;
ll phi[k],mu[k];
unordered_map<int,ll> mp1,mp2;
void init(){
	mu[1]=1,phi[1]=1;
	for(int i=2;i<k;i++){
		if(!vis[i]) p[++cnt]=i,mu[i]=-1,phi[i]=i-1;
		for(int j=1;j<=cnt&&i*p[j]<k;j++){
			vis[i*p[j]]=1;
			if(i%p[j]==0){
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
			mu[i*p[j]]=-mu[i];
			phi[i*p[j]]=phi[i]*(p[j]-1);
		}
	}
	for(int i=2;i<k;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
ll gphi(int n){
	if(n<k) return phi[n];
	if(mp1.count(n)) return mp1[n];
	ll ans=1ll*n*(n+1)/2;
	for(int l=2,r;l<=n;l=r+1){
		r=n/(n/l);
		ans-=gphi(n/l)*(r-l+1);
	}
	return mp1[n]=ans;
}
ll gmu(int n){
	if(n<k) return mu[n];
	if(mp2.count(n)) return mp2[n];
	ll ans=1;
	for(int l=2,r;l<=n;l=r+1){//服了,这里+1会炸int 
		r=n/(n/l);
		ans-=gmu(n/l)*(r-l+1);
	}
	return mp2[n]=ans;
}
signed main(){
	scanf("%lld",&T);
	init();
	while(T--){
		scanf("%lld",&n);
		printf("%lld %lld\n",gphi(n),gmu(n));
	}
	return 0;
} 

推式子方法:(以i=1nφ(i2)为例)

根据题目要求,得出f(i)=φ(i2)=φ(i)i

S(n)=i=1nf(i)

此时我们需要构造一个g(x)使得h(i)=(fg)(i)的前缀和好求

写出形式,带入f(x)

h(i)=(fg)(i)

=d|if(d)g(id)

=d|iφ(d)dg(id)

此时又有欧拉函数的性质,i=d|iφ(d)

那么令g(x)=x即可

h(i)=d|iφ(d)did=i2

此时我们所需要的函数都求出来了,带入杜教筛式子g(1)S(n)=i=1nh(i)i=2ng(i)S(ni)即可

叁、Min_25筛

和杜教筛一样,可以在压线性的复杂度下求出积性函数的前缀和,时间复杂度 O(n34logn)

它的使用条件是:f(x) 是一个积性函数; f(x) 在质数处取值可以被简单算出; 对于质数p,f(pc) 是一个关于p的低阶多项式

step 1 分类

Min_25筛能在亚线性的复杂度下求出答案,其关键步骤就是按质数和合数分类,并枚举合数的最小质因子和质因子个数,即

i=1nf(i)=p is a primenf(p)+p is a primenf(pe)(minp(i)>pnpef(i))

其中 minp(i) 表示 i 的最小质因子。注意到一个合数的最小质因子一定小于 n ,枚举得到巨大优化。

step 2 质数求解

  • 注意,这里 g 和后面的 s 不记1的贡献,原因会在后面提到

设对于质数 p,其中 f 在 p 处能被表示为 f(p)=aipi 的多项式,那么不妨对于每一项 pk ,先统计出 pk 再进行计算

注意到,这里 g 存的函数值并不直接是原函数的值,而是某个能简单算出的函数且它在质数处的取值和原函数相同。这里直接写成多项式的形式是因为这个转化比较常见。

考虑一个 dp (太神仙了难以想到),设 g(n,j) 表示 n 范围内,所有质数或最小质因子大于第j个质数的所有数的k次方之和。最后要求的就是 g(n,j) ,其中 pj 是最后一个 n 的质数

考虑从 g(n,j1) 转移到 g(n,j) ,此时需要减去最小质因子是 pj 的所有合数的贡献。

既然最小质因子是 pj ,又幂函数是一个完全积性函数,那么我们直接提出它,分类:

g(n,j)=g(n,j1)pjk(g(npj,j1)g(pj1,j1))

初始值:g(n,0)=i=2nik

从搜索的角度来看,我们会转移到的位置 n,np1,np1p2... ,又有 np1p2=np1p2

发现上式只用到形如 nxxn 位置上的 dp 值,这些值最多只有 2n 种,只需要求出它们的值并直接 dp 即可。(注意到质数 pj 的取值本来就在 n 以下,所以不用特殊处理

step 3 合数求解

s(n,j) 表示 n 范围内最小质因子大于 pj 的所有数的 f 值之和。接下来令 G(n) 表示 n 范围内所有质数的 f 值之和,从 g(n,j) 算出即可

同样的分类,质数贡献+合数贡献

s(n,j)=(G(n)G(pj))+j<kf(pke)(s(npke,k)+[e1])

注意枚举的 pknpken 边界条件。

上文所说不记1的贡献就是原因在此:首先,对于积性函数 f(1)=1。则当 e=1 时,pke 就是一个质数,已经统计过了,不能再加;当 e1 时,需要加上 pke 的贡献。

(气死了,刚打完结果可能因为latex太多被卡退了,啥都没了全部重写……所以要养成随时保存的好习惯哦

一些实现细节看代码吧

Min_25筛模版
//Min_25筛模版 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5,inf=2e9,mod=1e9+7,inv2=500000004,inv6=166666668;
int n,m,p[maxn],cnt,vis[maxn];
void init(int n){
	for(int i=2;i<=n;i++){
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;p[j]*i<=n;j++){
			vis[p[j]*i]=1;
			if(i%p[j]==0) break;
		}
	}
}
int ind1[maxn],ind2[maxn];
//dp数组显然不能直接开到n,可以用map,但常数较大;这里用ind1 ind2分别存储x和n/x在dp数组的编号,下标<=sqrt(n)
int g1[maxn],g2[maxn],val[maxn],tot;
//一次项和二次项之和  所有n/x的值
int getid(int x){
	return x<=m?ind1[x]:ind2[n/x];
} 
int f(int x){
	x%=mod;//细节 
	return (x*x-x)%mod;
}
int s(int n,int j){//递归求解s(n,j) 
	if(p[j]>n) return 0;//边界
	int id1=getid(n),id2=getid(p[j]);
	int res=((g2[id1]-g1[id1])-(g2[id2]-g1[id2])+mod+mod)%mod;
	for(int k=j+1;k<=cnt&&p[k]<=n/p[k];k++){
		for(int e=1,num=p[k];num<=n;e++,num=num*p[k]){
			res=(res+f(num)*(s(n/num,k)+(e!=1)))%mod;
		}
	} 
	return res;
}
signed main(){
	scanf("%lld",&n);
	m=sqrt(n);
	init(m);
	for(int l=1,r;l<=n;l=r+1){//整除分块 
		r=n/(n/l);
		val[++tot]=n/l;
		if(val[tot]<=m) ind1[val[tot]]=tot;
		else ind2[n/val[tot]]=tot;
		int num=val[tot]%mod;//注意要取模!
		g1[tot]=num*(num+1)%mod*inv2%mod-1;
		g2[tot]=num*(num+1)%mod*(2*num+1)%mod*inv6%mod-1;//减掉1的贡献 
	}
	for(int j=1;j<=cnt;j++){//先枚举第二维再枚举第一维 
		for(int i=1;i<=tot&&p[j]<=val[i]/p[j];i++){
			int id1=getid(val[i]/p[j]),id2=getid(p[j-1]);//这样才能保证这里调用的g[id1]是存的j-1的信息,否则它都全被更新完了 
			g1[i]=(g1[i]-p[j]*(g1[id1]-g1[id2])%mod+mod)%mod;
			g2[i]=(g2[i]-p[j]*p[j]%mod*(g2[id1]-g2[id2])%mod+mod)%mod;
		}
	}
	printf("%lld",(s(n,0)+1)%mod);//最后加上1的贡献 
	return 0;
}

例题

[loj6235] 区间素数个数

因为Min_25筛是质数合数分开处理的,所有实际上我们只需要用第一步dp求出质数的0次方和即可

[loj053] 简单的函数

2的情况是特殊的,剩下在质数处的取值都是p-1。细节上处理一下即可。

注意到 p(p1) 并不是完全积性函数,要分别处理(我居然错在这儿了!!

本文作者:YYYmoon

本文链接:https://www.cnblogs.com/YYYmoon/p/18620364

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   YYYmoon  阅读(25)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起