各种反演细节梳理&模板

炫酷反演魔术课件byVFK
stO FDF Orz(证明全有%%%)

莫比乌斯反演

F(n)=d|nf(d)f(n)=d|nμ(nd)F(d)
F(n)=n|df(d)f(n)=n|dμ(dn)F(d)
推带gcd的题常用式子:(实际上是借用了积性函数的式子)
[gcd(i,j)==1]=d|gcd(i,j)μ(d)
gcd(i,j)=d|i,d|jφ(d)


洛谷P3455 [POI2007]ZAP-Queries
f(n)=i=1aj=1b[gcd(i,j)==n]
1.老实反演
F(n)=n|df(d)=anbn
f(n)=n|dμ(dn)adbd=d=1min(a,b)nμ(d)andbnd
2.套式子
f(n)=i=1anj=1bn[gcd(i,j)==1]=d|gcd(i,j)i=1anj=1bnμ(d)=d=1min(a,b)nμ(d)andbnd
整除分块,时间复杂度O(n)

#include<bits/stdc++.h>
#define LL long long
#define RG register
#define R RG int
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
using namespace std;
const int SZ=1<<19,N=50009;
int u[N],pr[N],p;bool f[N];
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int in(){
	G;while(*ip<'-')G;
	R x=*ip&15;G;
	while(*ip>'-'){x*=10;x+=*ip&15;G;}
	return x;
}
int main(){
	u[1]=f[1]=1;
	for(R i=2;i<N;++i){
		if(!f[i])u[pr[++p]=i]=-1;
		for(R j=1;j<=p&&i*pr[j]<N;++j){
			f[i*pr[j]]=1;
			if(i%pr[j]==0)break;
			u[i*pr[j]]=-u[i];
		}
		u[i]+=u[i-1];
	}
	for(R n=in();n;--n){
		R a=in(),b=in(),d=in(),lim=min(a,b)/d,ans=0;
		for(R l=1,r;l<=lim;l=r+1){
			r=min(a/(a/l),b/(b/l));
			ans+=(u[r]-u[l-1])*(a/(d*l))*(b/(d*l));
		}
		printf("%d\n",ans);
	}
	return 0;
}

洛谷P1829 [国家集训队]Crash的数字表格 / JZPTAB

i=1nj=1mijgcd(i,j)=k=1ni=1nj=1mijk[gcd(i,j)==k]=k=1ni=1nkj=1mkijk[gcd(i,j)==1]=k=1nki=1nkj=1mkd|gcd(i,j)μ(d)ij=k=1nkd=1nkμ(d)i=1nkdj=1mkdd2ij=k=1nkd=1nkd2μ(d)nkd(nkd+1)2mkd(mkd+1)2

#include<bits/stdc++.h>
#define LL long long
#define RG register
#define R RG int
#define YL 20101009
using namespace std;
const int N=1e7+9;
int p,pr[N/10],mu[N],s[N];bitset<N>np;
inline LL Sum(LL x){
	return x*(x+1)%YL*((YL+1)>>1)%YL;
}
LL Calc(R n,R m){
	LL ans=0;
	for(R d=1,r;d<=n;d=r+1){
		r=min(n/(n/d),m/(m/d));
		ans=(ans+(s[r]-s[d-1]+YL)*Sum(n/d)%YL*Sum(m/d))%YL;
	}
	return ans;
}
int main(){
	R n,m;LL ans=0;
	cin>>n>>m;
	if(n>m)swap(n,m);
	s[1]=1;
	for(R i=2;i<=n;++i){
		if(!np[i])pr[++p]=i,mu[i]=-1;
		s[i]=(s[i-1]+(LL)i*i%YL*mu[i]+YL)%YL;
		for(R j=1;j<=p&&i*pr[j]<=n;++j){
			np[i*pr[j]]=1;
			if(i%pr[j]==0)break;
			mu[i*pr[j]]=-mu[i];
		}
	}
	for(R k=1,r;k<=n;k=r+1){
		r=min(n/(n/k),m/(m/k));
		ans=(ans+(Sum(r)-Sum(k-1)+YL)*Calc(n/k,m/k))%YL;
	}
	cout<<ans<<endl;
	return 0;
}

然鹅BZOJ的JZPTAB有多次询问咋办?
详细题解by hbyer
有一个新的套路:枚举重复出现的kd(令其为t

t=1nnt(nt+1)2mt(mt+1)2d|ttdμ(d)

后面那个d|ttdμ(d)看成关于t的函数,暴力推一推发现是个积性函数,筛一下就好了。


洛谷P3704 [SDOI2017]数字表格
跟上一题一样的套路

i=1nj=1mFibgcd(i,j)=k=1nFibkd=1nμ(d)nkdmkd=t=1nk|tFibkμ(tk)ntmt=t=1n(k|tFibkμ(tk))ntmt

中间那块预处理前缀积,外面数论分块即可。

二项式反演

不常用但是好看:f(n)=i=0n(1)i(ni)g(i)g(n)=i=0n(1)i(ni)f(i)
正好难算,至多好算:f(n)=i=0n(ni)g(i)g(n)=i=0n(1)ni(ni)f(i)
正好难算,至少好算:f(k)=i=kn(ik)g(i)g(k)=i=kn(1)ik(ik)f(i)

错排

f(n)为至多n元素错排方案数也就是全排列方案数n!g(n)n元素错排方案数。
通过枚举排列中错位的元素个数i,有f(n)=i=0n(ni)g(i)
直接套第二个式子即可得g(n)=i=0n(1)ni(ni)f(i)=n!i=0n(1)i1i!

又一个例题

洛谷P4859 已经没有什么好害怕的了
套第三个式子。然并卵关键要想到如何DP和转化

#include<bits/stdc++.h>
#define LL long long
#define RG register
#define R RG int
using namespace std;
const LL N=2009,YL=1e9+9;
int a[N],b[N],ff[N],gg[N],F[N],I[N];
int Pow(LL b,R k,LL a=1){
    for(;k;k>>=1,b=b*b%YL)
        if(k&1)a=a*b%YL;
    return a;
}
int main(){
    R n,k,ans=0,*f=ff,*g=gg;
    cin>>n>>k;k=(n+k)/2;
    for(R i=1;i<=n;++i)cin>>a[i];
    for(R i=1;i<=n;++i)cin>>b[i];
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    f[0]=g[0]=F[0]=1;
    for(R i=1;i<=n;++i){
        R pos=lower_bound(b+1,b+n+1,a[i])-b-1;
        for(R j=1;j<=i;++j)
            g[j]=(f[j]+(LL)f[j-1]*max(pos-j+1,0))%YL;
        swap(f,g);
    }
    for(R i=1;i<=n;++i)F[i]=(LL)F[i-1]*i%YL;
    I[n]=Pow(F[n],YL-2);
    for(R i=n;i;--i)I[i-1]=(LL)I[i]*i%YL;
    for(R i=k;i<=n;++i)
        ans=(ans+(LL)((i-k)&1?YL-1:1)*F[i]%YL*I[k]%YL*I[i-k]%YL*F[n-i]%YL*f[i])%YL;
    cout<<ans<<endl;
    return 0;
}

斯特林反演

十分像二项式反演。。。
正好难算,至多好算:f(n)=i=0nS(n,i)g(i)g(n)=i=0n(1)nis(n,i)f(i)
正好难算,至少好算:f(k)=i=knS(i,k)g(i)g(k)=i=kn(1)iks(i,k)(i)
BZOJ4671 异或图
套第二个式子。然并卵关键要想到子集划分计数和线性基
完整题解:stO yyb Orz

#include<bits/stdc++.h>
#define LL long long
#define RG register
#define R RG int
using namespace std;
const int S=69,N=19;
char str[S];bool g[S][N][N];
int s,n,c[N];
LL ans,lb[S],fac[N];
void calc(R t){
	R cnt=0;memset(lb,0,sizeof(lb));
	for(R k=1;k<=s;++k){
		R p=0;LL now=0;
		for(R i=1;i<=n;++i)
			for(R j=i+1;j<=n;++j)
				if(c[i]!=c[j])now|=(LL)g[k][i][j]<<p++;
		for(R i=0;i<p;++i)
			if(1ll<<i&now){
				if(lb[i])now^=lb[i];
				else{lb[i]=now;++cnt;break;}
			}
	}
	ans+=(1&t?1:-1)*fac[t-1]*(1ll<<(s-cnt));
}
void dfs(R x,R t){
	if(x>n)return calc(t-1);
	for(R&i=c[x]=1;i<=t;++i)dfs(x+1,t+(t==i));
}
int main(){
	cin>>s;
	for(R k=1;k<=s;++k){
		cin>>str;n=(1+sqrt(1+8*strlen(str)))/2;
		for(R i=1,p=0;i<=n;++i)
			for(R j=i+1;j<=n;++j)
				g[k][i][j]=str[p++]&1;
	}
	for(R i=fac[0]=1;i<=n;++i)fac[i]=fac[i-1]*i;
	dfs(1,1);
	cout<<ans<<endl;
	return 0;
}

最值反演(min-max容斥)

max{S}=TS(1)|T|+1min{T}
min{S}=TS(1)|T|+1max{T}
在期望意义下仍然成立,即
E(max{S})=TS(1)|T|+1E(min{T})
E(min{S})=TS(1)|T|+1E(max{T})


推广到求第k大,只有大小k的子集产生贡献
maxk{S}=TS(1)|T|k(|T|1k1)min{T}
mink{S}=TS(1)|T|k(|T|1k1)max{T}


洛谷P3175 [HAOI2015]按位或
裸题,FWT之后套第三个式子即可

#include<bits/stdc++.h>
#define R register int
using namespace std;
double EPS=1e-10,a[1<<20];
int c[1<<20];
int main(){
	R n,m,s=0;
	cin>>n;m=1<<n;
	for(R i=0;i<m;++i){
		cin>>a[i];
		if(a[i]>EPS)s|=i;
		c[i]=c[i>>1]^(1&i);
	}
	if(s!=m-1)return puts("INF"),0;
	for(R i=1;i<m;i<<=1)
		for(R j=0;j<m;j+=i<<1)
			for(R k=0;k<i;++k)
				a[k+j+i]+=a[k+j];
	double ans=0;
	for(R i=1;i<m;++i)
		if(a[i]>EPS)ans+=(c[i]?1:-1)/(1-a[i^(m-1)]);
	return printf("%.10lf\n",ans),0;
}

洛谷P4707 重返现世
套第五个式子,然并卵关键要想到如何DP出容斥系数

#include<bits/stdc++.h>
#define LL long long
#define RG register
#define R RG int
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
using namespace std;
const int SZ=1<<19,N=10009,YL=998244353;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
int f[N][12];LL inv[N];
inline int in(){
	G;while(*ip<'-')G;
	R x=*ip&15;G;
	while(*ip>'-'){x*=10;x+=*ip&15;G;}
	return x;
}
inline int add(R x,R y){static int z;z=x+y;return z<YL?z:z-YL;}
inline int sub(R x,R y){static int z;z=x-y;return z<0?z+YL:z;}
int main(){
	R n=in(),s=n-in()+1,m=in();
	f[0][0]=1;
	for(R i=1;i<=n;++i){
		R p=in();
		for(R j=m-p;~j;--j)
			for(R k=s;k;--k)
				f[j+p][k]=add(f[j+p][k],sub(f[j][k-1],f[j][k]));
	}
	inv[1]=1;LL ans=f[1][s];
	for(R i=2;i<=m;++i){
		inv[i]=(YL-YL/i)*inv[YL%i]%YL;
		ans=(ans+f[i][s]*inv[i])%YL;
	}
	cout<<ans*m%YL<<endl;
	return 0;
}

子集反演

感觉就跟高维差分/IFWT一样?
正好难算,至多好算:f(S)=TSg(T)g(S)=TS(1)|S||T|f(T)
正好难算,至少好算:f(S)=STg(T)g(S)=ST(1)|T||S|f(T)


还有推广到多重集的子集反演。
μ(S)=(1)|S|[S]
(原来莫比乌斯函数就是把整数看作其质因数集合而得来的反演系数啊)
f(S)=TSg(T)g(S)=TSμ(ST)f(T)
f(S)=STg(T)g(S)=STμ(TS)f(T)


子集卷积:已知a,b,求c使得cS=TSaTbST(注意和高维前缀和不一样!)
与高维前缀和的区别主要在于集合大小的限制,只有|T|+|ST|=|S|才能产生贡献。
考虑把数组按集合大小分解,C|S|,S=TSA|T|,TB|ST|,ST
于是枚举i,j,将Ai,Bj的或卷积贡献到Ci+j中。最后把C反演回来就是我们要的c

单位根反演

不会
洛谷日报——单位根反演
单位根与其若干应用 by YCB

posted @   Flash_Hu  阅读(1220)  评论(2编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
西雅图
17:14发布
西雅图
17:14发布
6°
东南风
3级
空气质量
相对湿度
84%
今天
3°/13°
周六
小雨
6°/15°
周日
中雨
4°/15°