莫比乌斯反演
莫比乌斯函数
推导常用公式
若 ,则 ,这是莫比乌斯反演的正确公式,但是用这种方法很多时候特别难推,因此基本不会用到
,2和3公式通常会一起使用
为了好写,推导默认下取整
求
cpp
int f(int a,int b,int k){
int res=0;
a=a/k,b=b/k;
int n=min(a,b);
for(int l=1,r;l<=n;l=r+1){
r=min({n,a/(a/l),b/(b/l)});
res+=(sum[r]-sum[l-1])*(a/l)*(b/l);//sum为mu前缀和
}
return res;
}求
令,
可以调和级数预处理
cpp
int mu[N],sum[N];
bool st[N];
vector<int> prime;
void sieve(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
st[i]=1;
mu[i]=-1;
prime.push_back(i);
}
for(auto p:prime){
if(p*i>n)break;
st[p*i]=1;
if(i%p==0)break;
mu[p*i]=-mu[i];
}
}
for(auto p:prime){
for(int i=p;i<=n;i+=p)sum[i]+=mu[i/p];
}
for(int i=1;i<=n;i++){
sum[i]+=sum[i-1];
}
}
int f(int a,int b){
int res=0;
int n=min(a,b);
for(int l=1,r;l<=n;l=r+1){
r=min({n,a/(a/l),b/(b/l)});
res+=(sum[r]-sum[l-1])*(a/l)*(b/l);
}
return res;
}
void solve(){
int a,b,k;
cin>>a>>b;
cout<<f(a,b)<<"\n";
}求
定义
分块套分块解决,复杂度为
cpp
//P1829
int n,m;
int mu[N],sum[N];
bool vis[N];
vector<int> prime;
void sieve(){
mu[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
vis[i]=1;
prime.push_back(i);
mu[i]=-1;
}
for(int j=0;j<prime.size()&&prime[j]*i<N;j++){
vis[prime[j]*i]=1;
if(i%prime[j]==0)break;
mu[prime[j]*i]=-mu[i];
}
}
for(int i=1;i<N;i++)sum[i]=(sum[i-1]+mu[i]*i%mod*i%mod)%mod,sum[i]=(sum[i]+mod)%mod;
}
int G(int n,int m){
return n*(n+1)/2%mod*(m*(m+1)/2%mod)%mod;
}
int F(int n,int m){
int res=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
res=(res+((sum[r]-sum[l-1])%mod+mod)%mod*G(n/l,m/l)%mod)%mod;
}
return res;
}
int cal(int n,int m){
if(n>m)swap(n,m);
int res=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
res=(res+(r-l+1)*(l+r)/2%mod*F(n/l,m/l)%mod)%mod;
}
return res;
}
void solve(){
int n,m;
cin>>n>>m;
sieve();
cout<<cal(n,m)<<"\n";
}P3704 [SDOI2017] 数字表格
题意:
求
思路:
cpp
bool vis[N];
int mu[N],F[N],f[N],invf[N];
vector<int> prime;
int qpow(int a,int b){
int ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
void init(){
mu[1]=1;
f[1]=F[0]=F[1]=invf[0]=invf[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
prime.push_back(i);
mu[i]=-1;
}
for(int j=0;j<prime.size()&&i*prime[j]<N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
f[i]=(f[i-1]+f[i-2])%mod;
invf[i]=qpow(f[i],mod-2);
F[i]=1;
}
for(int i=1;i<N;i++){
for(int j=i;j<N;j+=i){
if(mu[j/i])F[j]=(F[j]*(mu[j/i]==1?f[i]:invf[i]))%mod;
}
}
for(int i=2;i<N;i++)F[i]=F[i]*F[i-1]%mod;
}
int cal(int n,int m){
if(n>m)swap(n,m);
int ans=1;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
int s=F[r]*qpow(F[l-1],mod-2)%mod;
ans=ans*qpow(s,(n/l)*(m/l))%mod;
}
return ans;
}
void solve(){
int n,m;
cin>>n>>m;
cout<<cal(n,m)<<"\n";
}P3327 [SDOI2015] 约数个数和
题意:
思路:
带入原式得,
cpp
bool vis[N];
int mu[N],d[N],t[N];//t为最小质因子出现次数
vector<int> prime;
void init(){
mu[1]=d[1]=1;
for(int i=2;i<N;i++){
if(!vis[i]){
vis[i]=1;
prime.push_back(i);
mu[i]=-1;
d[i]=2;
t[i]=1;
}
for(int j=0;j<prime.size()&&i*prime[j]<N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
d[i*prime[j]]=d[i]/(t[i]+1)*(t[i]+2);
t[i*prime[j]]=t[i]+1;
break;
}
mu[i*prime[j]]=-mu[i];
d[i*prime[j]]=d[i]*2;
t[i*prime[j]]=1;
}
}
for(int i=2;i<N;i++){
d[i]+=d[i-1],mu[i]+=mu[i-1];
}
}
int cal(int n,int m){
if(n>m)swap(n,m);
int ans=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
ans+=(mu[r]-mu[l-1])*d[n/l]*d[m/l];
}
return ans;
}
void solve(){
int n,m;
cin>>n>>m;
cout<<cal(n,m)<<"\n";
}求
如果,如P3768,函数用杜教筛求解
cpp
vector<int> phi,sum_phi;
vector<int> prime;
vector<bool> st;
int P,inv2,inv6;
int qpow(int a,int b,int mod){
int ans=1;
for(;b;b>>=1){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
}
return ans;
}
void sieve(int n){
phi.resize(n+1);
sum_phi.resize(n+1);
st.resize(n+1);
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
prime.push_back(i);
phi[i]=i-1;
}
for(auto p:prime){
if(p*i>n)break;
st[p*i]=1;
if(i%p==0){
phi[p*i]=phi[i]*p;
break;
}
else{
phi[p*i]=phi[i]*(p-1);
}
}
}
for(int i=1;i<=n;i++){
sum_phi[i]=(sum_phi[i-1]+phi[i]*i%P*i%P)%P;
}
}
unordered_map<int,int> mp;
int G(int x){
x%=P;
return x*(x+1)%P*(2*x+1)%P*inv6%P;
}
int g(int x){
x%=P;
return x*(x+1)%P*inv2%P*x%P*(x+1)%P*inv2%P;
}
int S(int x){
if(x<sum_phi.size())return sum_phi[x];
if(mp.count(x))return mp[x];
int res=g(x);
int pre=1,cur;
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
cur=G(r);
res-=(cur-pre+P)%P*S(x/l)%P;
res=(res%P+P)%P;
pre=cur;
}
return mp[x]=(res%P+P)%P;
}
int f(int n){
int res=0;
int pre=0,cur;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
int cur=S(r);
res=(res+g(n/l)*(cur-pre+P)%P)%P;
pre=cur;
}
return (res%P+P)%P;
}
void solve(){
int n;
cin>>P>>n;
inv2=qpow(2,P-2,P);
inv6=qpow(6,P-2,P);
sieve(pow(n,2.0/3));
cout<<f(n)<<"\n";
}求
利用一个桶记录出现个数,表示的出现个数
反演得到
详见P3911和agc038c
cpp
// 求\sum_{i=1}^n\sum_{j=1}^n lcm(a[i],a[j])
int mu[N],sum[N],t[N],sum_t[N];
bool st[N];
vector<int> prime;
void sieve(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
prime.push_back(i);
mu[i]=-1;
}
for(auto x:prime){
if(x*i>n)break;
st[x*i]=1;
if(i%x==0){
mu[x*i]=0;
break;
}
else mu[x*i]=-mu[i];
}
}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){
sum[j]+=mu[i]*i;
}
}
}
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
t[x]++;
}
sieve(1e6);
int ans=0;
for(int T=1;T<=1e6;T++){
int res=0;
for(int i=1;i<=1e6/T;i++)res+=i*t[i*T];
ans+=T*res*res*sum[T];
}
cout<<ans<<"\n";
}