基础莫比乌斯反演

基础莫比乌斯反演#

莫比乌斯函数#

对于 μ(n)

n=1μ(n)=1

n=p1p2pk(其中 pi 为互异素数)μ(n)=(1)k

否则 μ(n)=0

线性筛莫比乌斯函数#

inline void get_mu(){
	mu[1]=not_p[1]=1;
	for(int i=2;i<N;++i){
		if(!not_p[i]){
			p[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*p[j]<N;++j){
			not_p[i*p[j]]=1;
			if(i%p[j]==0) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
}

基本公式#

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

so

d|gcd(i,j)μ(d)=[gcd(i,j)=1]

还有:

d|nμ(d)d=φ(n)n

p.s. 后文中除法均默认向下取整

对于约数个数函数 d(x) 有如下性质:

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

整除分块#

对于

i=1nni

因为有许多 ni 的值相同,每一个值相同的块最后一个数是 nni 因此可以把其分成 n 块再预处理

这个式子的结果可以在 O(n) 的时间内求出

for(int l=1,r;l<=n;l=r+1){
	r=n/(n/l);
	ans+=(r-l+1)*(n/l);
}

blog

P2257 YY的GCD#

题意

求:

i=1nj=1m[gcd(i,j)prime]

思路

枚举一个 k

k=1ni=1nj=1m[gcd(i,j)=k]  kprime

同时除以 k

k=1ni=1nkj=1mk[gcd(i,j)=1]  kprime

已知

d|gcd(i,j)μ(d)=[gcd(i,j)=1]

so,反演一下

k=1ni=1nkj=1mkd|gcd(i,j)μ(d)  kprime

枚举 d,由于 d|gcd(i,j),所以 i,j 均为 d 的倍数

k=1nd=1min(n,m)kμ(d)nkdmkd  kprime

但过不了,考虑优化

T=kd,有

k=1nd=1min(n,m)kμ(d)nTmT  kprime

枚举 T,往前提

T=1min(n,m)nTmTk|T,kprimeμ(Tk)

后面一大部分可以预处理

k|T,kprimeμ(Tk)

处理一下就可

注意直接枚举会 TLE

所以加一个整除分块O2就行

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

const int N=1e7+1;

#define int long long

int cnt;
int sum[N],f[N];
int mu[N],p[N];
bool not_p[N];

inline void get_mu(){
	mu[1]=not_p[1]=1;
	for(int i=2;i<N;++i){
		if(!not_p[i]){
			p[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*p[j]<N;++j){
			not_p[i*p[j]]=1;
			if(i%p[j]==0) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<=cnt;++i)
		for(int j=1;p[i]*j<N;++j)
			f[j*p[i]]+=mu[j];
	for(int i=1;i<N;++i)
		sum[i]=sum[i-1]+f[i];
}

inline int solve(int x,int y){
	int ans=0;
	if(x>y) swap(x,y);
	for(int i=1,j=0;i<=x;i=j+1){
		j=min(x/(x/i),y/(y/i));
		ans+=(sum[j]-sum[i-1])*(x/i)*(y/i);
	}
	return ans;
}

signed main(){
	int T=read();
	get_mu();
	while(T--) cout<<solve(read(),read())<<endl;
}

P3455 ZAP-Queries#

题意

给你 m,n,k,求

i=1nj=1m[gcd(i,j)=k]

思路

和之前几乎一摸一样

i=1nj=1m[gcd(ik,jk)=1]

i=1nkj=1mk[gcd(i,j)=1]

i=1nkj=1mkd|gcd(i,j)μ(d)

d=1min(n,m)μ(d)nkdmkd

用一个整除分块搞搞就完事

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

const int N=5e4+1;

int sum[N];
int cnt,mu[N],p[N];
bool not_p[N];

inline void get_mu(){
	mu[1]=not_p[1]=1;
	for(int i=2;i<N;++i){
		if(!not_p[i]){
			p[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*p[j]<N;++j){
			not_p[i*p[j]]=1;
			if(i%p[j]==0) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<N;++i) sum[i]=sum[i-1]+mu[i];
}

inline int solve(int x,int y,int k){
	int ans=0;
	if(x>y) swap(x,y);
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=(sum[r]-sum[l-1])*(x/(l*k))*(y/(l*k));
	}
	return ans;
}

signed main(){
	get_mu();
	int T=read();
	while(T--){
		int x=read(),y=read(),k=read();
		cout<<solve(x,y,k)<<endl;
	}
}

P2522 Problem b#

题意

i=abj=cd[gcd(i,j)=k]

思路

不再推一遍了

P3455

但是有一些不同的是,这题多一个容斥

假如 ans(b,d,k) 是式子

i=1bj=1d[gcd(i,j)=k]

的值

那么最终的结果 Ans=ans(b,d,k)ans(c1,b,k)ans(a1,d,k)+ans(a1,c1,k)

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

const int N=5e4+1;

int sum[N];
int cnt,mu[N],p[N];
bool not_p[N];

inline void get_mu(){
	mu[1]=not_p[1]=1;
	for(int i=2;i<N;++i){
		if(!not_p[i]){
			p[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*p[j]<N;++j){
			not_p[i*p[j]]=1;
			if(i%p[j]==0) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<N;++i) sum[i]=sum[i-1]+mu[i];
}

inline int solve(int x,int y,int k){
	int ans=0;
	if(x>y) swap(x,y);
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=(sum[r]-sum[l-1])*(x/(l*k))*(y/(l*k));
	}
	return ans;
}

signed main(){
	get_mu();
	int T=read();
	while(T--){
		int a=read(),b=read(),c=read(),d=read(),k=read();
		cout<<solve(b,d,k)-solve(b,c-1,k)-solve(a-1,d,k)+solve(a-1,c-1,k)<<endl;
	}
}

P3327 约数个数和#

题意

d(x)x 的约数个数,给定 n,m,求

i=1nj=1md(ij)

思路

已知

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

so,要求的就是

i=1nj=1mx|iy|j[gcd(x,y)=1]

直接枚举 i,j 的因数

i=1nj=1mnimj[gcd(i,j)=1]

反演一下

i=1nj=1md|gcd(i,j)nimjμ(d)

改为枚举 d

d=1min(m,n)μ(d)i=1nj=1mnimj[d|gcd(i,j)]

减少枚举次数

d=1min(m,n)μ(d)i=1ndj=1mdndimdj

结合一下

d=1min(m,n)μ(d)(i=1ndndi)(j=1mdmdj)

接下来预处理一个函数 f(x)=i=1xxi,则

ans=d=1min(m,n)μ(d)f(nd)f(md)

然后整除分块就行

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

const int N=5e4+1;

#define int long long

int cnt;
int sum[N],f[N];
int mu[N],p[N];
bool not_p[N];

inline void get_mu(){
	mu[1]=not_p[1]=1;
	for(int i=2;i<N;++i){
		if(!not_p[i]){
			p[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*p[j]<N;++j){
			not_p[i*p[j]]=1;
			if(i%p[j]==0) break;
			else mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<N;++i) sum[i]=sum[i-1]+mu[i];
	for(int i=1;i<N;++i)
		for(int l=1,r;l<=i;l=r+1){
			r=i/(i/l);
			f[i]+=(r-l+1)*(i/l);
		}
}

inline int solve(int x,int y){
	int ans=0;
	if(x>y) swap(x,y);
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=(sum[r]-sum[l-1])*f[x/l]*f[y/l];
	}
	return ans;
}

signed main(){
	int T=read();
	get_mu();
	while(T--){
		int n=read(),m=read();
		cout<<solve(n,m)<<endl;
	}
}

作者:Into_qwq

出处:https://www.cnblogs.com/into-qwq/p/16446954.html

版权:本作品采用「qwq」许可协议进行许可。

posted @   Into_qwq  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示