莫比乌斯反演
前言
反演实际上是利用莫比乌斯函数实现
和函数与原函数之间的转换
我们需要求原函数,而实际上和函数是容易求得的
常用反演规律
表示..为n的方案数
表示..为n的倍数的方案数
例题
公约数的和
可以当作模版题 这一题还可以对式子进行变换,对于j=i+1这种和式超级难受
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void solve(){ cin>>n; getMu(); int ans=0; rep(i,1,n){ F[i]=0; for(int j=i;j<=n;j+=i){ F[i]+=(n/i-j/i); } } rep(i,1,n){ f[i]=0; for(int d=i;d<=n;d+=i){ f[i]+=F[d]*mu[d/i]; } ans+=f[i]*i; } cout<<ans<<endl; }
|
式子变换之后的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void solve(){ cin>>n; init(); int ans=0; rep(i,1,n) F[i]=(n/i)*(n/i); rep(i,1,n){ f[i]=0; for(int d=i;d<=n;d+=i){ f[i]+=F[d]*mu[d/i]; } ans+=f[i]*i; } ans-=(1+n)*n/2; ans/=2; cout<<ans<<endl; }
|
能量采集
序列
f(n)表示gcd为n的方案数,F(n)表示gcd为n的倍数的方案数,所求答案即为f(1)
跳蚤
相同的方法反演,使用乘法原理就可以算出gcd为k的倍数的方案数
公约数之和加强版
LCMS
和式可以变换为
上面的式子是比较好算的,交叉相乘为和的乘积,利用反演将原函数用上述和函数表示
最小公倍数之和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| void solve(){ cin>>n; init(N-10); int sum=0,mx=0; rep(i,1,n){ cin>>a[i]; cnt[a[i]]++; mx=max(mx,a[i]); sum+=a[i]; sum%=p; } n=mx; rep(i,1,n){ for(int j=i;j<=n;j+=i){ F[i]+=cnt[j]*j; F[i]%=p; } F[i]*=F[i]; F[i]%=p; } int ans=0; rep(i,1,n){ for(int d=i;d<=n;d+=i){ f[i]=(f[i]+F[d]*mu[d/i]%p+p)%p; } ans=(ans+f[i]*inv[i]%p+p)%p; } ans=(ans-sum+p)%p; ans=ans*inv[2]%p; cout<<ans<<endl; }
|
分块优化
ZAP Queries
显然是非常好计算的,但是在多case的情况下,如果不进行优化很容易超时
优化前
1 2 3 4
| for(int i=d;i<=min(a,b);i+=d){ F[i]=(a/i)*(b/i); ans+=F[i]*mu[i/d]; }
|
发现F[i]其实可以分块来做,在一定区间内,F[i]的值是不变的,统计莫比乌斯函数区间和即可
t时枚举量,实际上除数是a/d,b/d
1 2 3 4
| for(int l=1,r;l<=min(a/d,b/d);l=r+1){ r=min(a/d/(a/d/l),b/d/(b/d/l)); ans+=(a/(l*d))*(b/(l*d))*(mu[r]-mu[l-1]); }
|
1 2 3 4 5
| a/=d,b/=d; for(int l=1,r;l<=min(a,b);l=r+1){ int r=min(a/(a/l),b/(b/l)); ans+=(a/l)*(b/l)*(sum[r]-sum[l-1]); }
|
数表
不需要确保枚举量是d的倍数,使用树状数组求前面一部分前缀和,只有d的倍数才会被添加到树状数组中
离线查找答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| struct node{ int sum,val; bool operator<(const node& o)const{ return sum<o.sum; } }b[N]; struct QUERY{ int n,m,a,id; bool operator<(const QUERY& o)const{ return a<o.a; } }Q[N]; int solve2(int n,int m){ int t=min(n,m),ans=0; for(int l=1,r;l<=t;l=r+1){ r=min(n/(n/l),m/(m/l)); ans+=(query(r)-query(l-1))*(n/l)*(m/l); } return ans; } int ans[N]; void solve(){ int q; cin>>q; rep(i,1,q){ cin>>Q[i].n>>Q[i].m>>Q[i].a; Q[i].id=i; } sort(b+1,b+100001); sort(Q+1,Q+1+q); for(int i=1,j=1;i<=q;i++){ while(b[j].sum<=Q[i].a and j<=1e5){ for(int d=b[j].val;d<=1e5;d+=b[j].val){ add(d,b[j].sum*mu[d/b[j].val]); } j++; } ans[Q[i].id]=solve2(Q[i].n,Q[i].m); } rep(i,1,q){ if(ans[i]<0) ans[i]+=pow(2,31); cout<<ans[i]<<endl; } }
|