不相信自己的人,连努力的价值都没有。|

Code_AC

园龄:3年粉丝:5关注:3

莫比乌斯反演小记

基本内容

莫比乌斯函数

μ 定义为 1 的逆。

一些小性质:

  1. μ1=ϵ
  2. μid=φ

反演内容

我的理解是:

[a=1]=d|aμ(d)

典型例题

例1 P2398 GCD SUM

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

来推下式子:

先枚举 gcd

d=1ni=1nj=1n[gcd(i,j)=d]

再将 d 除掉:

d=1ndi=1ndj=1nd[gcd(i,j)=1]

然后莫比乌斯反演:

d=1ndi=1ndj=1ndk|gcd(i,j)μ(k)

枚举 k

k=1nμ(k)d=1ndi=1ndj=1nd[k|gcd(i,j)]

k 除掉:

k=1nμ(k)d=1ndi=1ndkj=1ndk1

T=dk,整理一下:

T=1nnT2k|Tμ(k)(Tk)

由于 μid=φ

T=1nnT2φ(T)

欧拉函数可以直接线性筛预处理前缀和。中间的可以用整除分块 Θ(n)解决。

时间复杂度瓶颈在线性筛的 Θ(n)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=2e5+5;
int n,cnt;
int prime[MAXN],vis[MAXN],phi[MAXN],sum[MAXN];
inline void init(int n)
{
memset(vis,0,sizeof(vis));
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
prime[++cnt]=vis[i]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt && i*prime[j]<=n;j++)
{
vis[i*prime[j]]=prime[j];
if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1);
else phi[i*prime[j]]=phi[i]*prime[j];
}
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i];
return;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
init(n);
int ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans+=(sum[r]-sum[l-1])*(n/l)*(n/l);
}
printf("%lld\n",ans);
return 0;
}

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

求:

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

这道题与上题差不多,因为 lcm(i,j)=i×jgcd(i,j)

推式子:

N=min(n,m)

i=1nj=1mlcm(i,j)=i=1Nj=1Nijgcd(i,j)=d=1Ni=1Nj=1Nij[gcd(i,j)=d]=d=1Ni=1Nj=1Nij[gcd(i,j)=d]=d=1Ndi=1Ndj=1Ndij[gcd(i,j)=1]=d=1Ndi=1Ndj=1Ndijk|gcd(i,j)μ(k)=k=1Nμ(k)d=1Ndi=1Ndj=1Ndij[k|gcd(i,j)]=k=1Nμ(k)k2d=1Ndi=1Ndkj=1Ndkij

然后到这里就可以做了,莫比乌斯函数预处理,然后两次整除分块,时间复杂度 Θ(n)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e7+5;
const int MOD=20101009;
int n,m,cnt;
int prime[MAXN],vis[MAXN],mu[MAXN],sum[MAXN];
inline void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i]) {mu[i]=-1;prime[++cnt]=i;}
for(int j=1;j<=cnt && i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(!(i%prime[j])) {mu[i*prime[j]]=0;break;}
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+i*i%MOD*(mu[i]+MOD)%MOD)%MOD;
return;
}
inline int S(int r,int l=1) {return ((r+l)*(r-l+1)/2)%MOD;}
inline int Sum(int x,int y) {return ((x*(x+1)/2)%MOD)*((y*(y+1)/2)%MOD)%MOD;}
inline int solve(int x,int y)
{
int ans=0;
for(int l=1,r;l<=min(x,y);l=r+1)
{
int r1=x/(x/l),r2=y/(y/l);
r=min(r1,r2);
ans=(ans+(sum[r]-sum[l-1]+MOD)%MOD*Sum(x/r,y/r)%MOD)%MOD;
}
return ans;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m; init(min(n,m));
int ans=0;
for(int l=1,r;l<=min(n,m);l=r+1)
{
int r1=n/(n/l),r2=m/(m/l);
r=min(r1,r2);
ans=(ans+S(r,l)*solve(n/r,m/r)%MOD)%MOD;
}
printf("%lld\n",(ans+MOD)%MOD);
return 0;
}

例3 P3327 [SDOI2015] 约数个数和

这道题有个前置知识:

d(ij)=x|iy|j[gcd(x,y)=1]

然后就来推式子:

N=min(n,m)

i=1Nj=1Nx|iy|j[gcd(i,j)=1]=i=1Nj=1Nnimj[gcd(i,j)=1]=i=1Nj=1Nnimjd|gcd(i,j)μ(d)=d=1Nμ(d)i=1Nj=1Nnimj[d|gcd(i,j)]=d=1Nμ(d)i=1Nij=1Ninidmjd

那么到这里就可以做了。

显然 s(x)=i=1xxi 可以整除分块预处理,那么每组数据计算只需要用一次整除分块,整体时间复杂度 Θ(Tn)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e4+5;
int T,n,m,cnt;
int prime[MAXN],vis[MAXN],mu[MAXN],summu[MAXN],sum[MAXN];
inline void init(int n)
{
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i]) {mu[i]=-1;prime[++cnt]=i;}
for(int j=1;j<=cnt && i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(!(i%prime[j])) {mu[i*prime[j]];break;}
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) summu[i]=summu[i-1]+mu[i];
for(int i=1;i<=n;i++)
{
int ans=0;
for(int l=1,r;l<=i;l=r+1)
r=i/(i/l),ans+=(r-l+1)*(i/l);
sum[i]=ans;
}
return;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T; init(MAXN);
while(T--)
{
cin>>n>>m; int ans=0;
for(int l=1,r;l<=min(n,m);l=r+1)
{
int r1=n/(n/l),r2=m/(m/l);
r=min(r1,r2);
ans+=(summu[r]-summu[l-1])*sum[n/l]*sum[m/l];
}
printf("%lld\n",ans);
}
return 0;
}

本文作者:Code_AC

本文链接:https://www.cnblogs.com/code-ac/p/17734177.html

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

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