莫反

之前做了些模板式的题,但是还是没有掌握多少,再总结总结。
莫比乌斯反演,基本形式:

\[g(k)=\sum_{i|k}f(i)\to f(k)=\sum_{i|k}g(i)\mu(\frac{k}{i}) \]

证明是:

\[\sum_{i|k}g(i)\mu(\frac{k}{i})=\sum_{i|k}\sum_{p|\frac{k}{i}}f(p)\mu(i)=\sum_{p|k}f(p)\sum_{i|\frac{k}{p}}\mu(i)=f(k) \]

这个证明注意一点即可,自己推导时要让两个\(\sum\)枚举互斥的两个部分。
这一点在计数题里面尝尝用来宽松限制条件,和容斥在计数上的效果差不多。
比方说一个题目要求枚举一个集合,集合中的元素lcm严格等于一个定值k,总共的集合数目(其他条件省略)。
那么我们就可以设\(f(k)\)表示lcm等于定值k的方案数,然后对应一个\(g(k)=\sum_{i|k}f(i)\)表示所有lcm整除k的集合方案数。
然后先计算出g,根据上面的公式即可得到对应的f值,g怎么计算就要看题目中的条件了。
另外一个在计数题中常应用的是:

\[\sum_{i|k}\mu(i)=[k=1] \]

这个就是在出现gcd=1的时候可以应用的技巧,把条件变换,优先枚举gcd然后计算。
这里就先放个计数题。

例题

给出集合S的gcd与lcm的和为m,总共有n个元素,问合法集合数。
考虑将问题先转换,lcm是gcd的倍数,设\(lcm=k* gcd\)\(d=gcd\)
\(m=(k+1)d\to k=(m-d)/d\)我们枚举d,所求即为:

\[\sum_{S|\forall i∈S\ d|i}[lcm_{\forall i∈S}=k* d,|S|=n] \]

这个形式不好看,发现d是可以整体除去的,所以就有:

\[\sum_{S}[lcm_{\forall i∈S}=k,gcd_{\forall i∈S}=1,|S|=n] \]

接着我们宽松限制条件,设上式为f:

\[g(k)=\sum_{i|k}f(i),f(k)=\sum_{i|k}g(i)\mu(\frac{k}{i}) \]

\[=\sum_{S}[lcm_{\forall i∈S}|k,gcd_{\forall i∈S}=1,|S|=n] \]

\[=\sum_{S}[\forall i∈S\ i|k,|S|=n]\sum_{d|gcd_{\forall i∈S}}\mu(d) \]

\[=\sum_{d|k}\mu(d)\sum_{S}[\forall i∈S\ d|i|k,|S|=n] \]

\[=\sum_{d|k}\mu(d)\sum_{S}[\forall i∈S\ i|\frac{k}{d},|S|=n] \]

\[=\sum_{d|k}\mu(d)\binom{d(\frac{k}{d})}{n} \]

这个d函数就是约数个数函数,设后面这个\(\binom{d(\frac{k}{d})}{n}\)\(h(n)\)
然后考虑把g代回f中:

\[f(k)=\sum_{i|k}\mu(\frac{k}{i})\sum_{d|i}\mu(d)\binom{d(\frac{k}{d})}{n} \]

发现这是多个函数相互卷\((\mu* \mu* h)\)
对于\((\mu* \mu)\)这个可以直接算,手膜一下。
\(\sum_{i|k}\mu(i)* \mu(\frac{k}{i})\)中,对于因子i来说,当其次数为1的时候,分解方法两种,对答案的贡献是-2.
当其次数是2时,贡献为1,因为三种分解方式,只有两边均分1时,\(\mu\)值均为-1,其余有零没意义。
再看次数是3及以上时,均为0,这时整体的答案就是0.
算完后与h暴力卷,所以单次算f复杂度是\(O(d(m)^2)\)
然后我们还枚举了m的因数,所以整体复杂度是\(O(d(m)^3)\)

点击查看代码
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
#define qr qr()
#define ps push_back
#define pa pair<int,int>
#define ve vector
#define fi first
#define se second
using namespace std;
const int N=2e5+200;
int n,m,mod,c[1050][1050];
inline ll qr{
	ll x=0;char ch=getchar();bool f=0;
	while(ch>57||ch<48)f=(ch=='-')?1:0,ch=getchar();
	while(ch>=48&&ch<=57)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
int d(int x){
	int ans = 1;
	for (int i=2;i*i<=x;++i){
		if(x%i)continue;
		int cnt=0;
		while(!(x%i))x/=i,++cnt;
		ans*=cnt+1;
	}return (x!=1)?ans*2%mod:ans;
}
int mu(int x){
	int ans=1;
	for(int i=2;i*i<=x;++i){
		if(x%i)continue;
		int cnt=0;
		while(!(x%i))x/=i,++cnt;
		if(cnt==1)ans=1ll*ans*(mod-2)%mod;
		else if(cnt>2)return 0;
	}return (x!=1)?(1ll*ans*(mod-2)%mod):ans;
}
int f(int x){
	int ans=0;
	for(int i=1;i*i<=x;++i){
		if(x%i)continue;
		int s=d(i);
		if(s>=n)ans=(1ll*mu(x/i)*c[s][n+1]+ans)%mod;
		if(i*i==x)continue;
		s=d(x/i);
		if(s>=n)ans=(1ll*mu(i)*c[s][n+1]+ans)%mod;
	}return ans;
}
void init(){
	n=qr;m=qr;mod=qr;
	c[0][1]=1;
	for(int i=1;i<=1000;++i)
		for(int j=1;j<=i+1;++j)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	int ans=0;
	for(int i=1;i*i<=m;++i){
		if(m%i)continue;
		ans=(ans+f(i-1))%mod;
		if(i*i==m)continue;
		ans=(ans+f(m/i-1))%mod;
	}cout<<ans;
}
int main(){
	// ios::sync_with_stdio(0);
	// cin.tie(0);
	// cout.tie(0);




	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);




	init();
	return 0;
}
posted @ 2024-07-28 16:33  SLS-wwppcc  阅读(25)  评论(0)    收藏  举报