莫反
之前做了些模板式的题,但是还是没有掌握多少,再总结总结。
莫比乌斯反演,基本形式:
证明是:
这个证明注意一点即可,自己推导时要让两个\(\sum\)枚举互斥的两个部分。
这一点在计数题里面尝尝用来宽松限制条件,和容斥在计数上的效果差不多。
比方说一个题目要求枚举一个集合,集合中的元素lcm严格等于一个定值k,总共的集合数目(其他条件省略)。
那么我们就可以设\(f(k)\)表示lcm等于定值k的方案数,然后对应一个\(g(k)=\sum_{i|k}f(i)\)表示所有lcm整除k的集合方案数。
然后先计算出g,根据上面的公式即可得到对应的f值,g怎么计算就要看题目中的条件了。
另外一个在计数题中常应用的是:
这个就是在出现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,所求即为:
这个形式不好看,发现d是可以整体除去的,所以就有:
接着我们宽松限制条件,设上式为f:
这个d函数就是约数个数函数,设后面这个\(\binom{d(\frac{k}{d})}{n}\)为\(h(n)\)。
然后考虑把g代回f中:
发现这是多个函数相互卷\((\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;
}