Loading

基础莫比乌斯反演

基础莫比乌斯反演

莫比乌斯函数

对于 \(\mu(n)\)

\(n=1\)\(\mu(n)=1\)

\(n=p_1\cdot p_2\cdots p_k(\text{其中}\ p_i\ \text{为互异素数})\)\(\mu(n)=(-1)^k\)

否则 \(\mu(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];
		}
	}
}

基本公式

\[\sum_{d|n}\mu(d)=[n=1] \]

so

\[\sum_{d|\gcd(i,j)}\mu(d)=[\gcd(i,j)=1] \]

还有:

\[\sum_{d|n}\frac{\mu(d)}{d}=\frac{\varphi(n)}{n} \]

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

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

\[d(i\cdot j)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

整除分块

对于

\[\sum_{i=1}^n \frac ni \]

因为有许多 \(\frac ni\) 的值相同,每一个值相同的块最后一个数是 \(\frac {n}{\frac{n}{i}}\) 因此可以把其分成 \(\sqrt n\) 块再预处理

这个式子的结果可以在 \(O(\sqrt{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

题意

求:

\[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)\in prime] \]

思路

枚举一个 \(k\)

\[\sum_{k=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k]\ \ k\in prime \]

同时除以 \(k\)

\[\sum_{k=1}^{n}\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac mk}[\gcd(i,j)=1]\ \ k\in prime \]

已知

\[\sum_{d|\gcd(i,j)}\mu(d)=[\gcd(i,j)=1] \]

so,反演一下

\[\sum_{k=1}^{n}\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac mk}\sum_{d|\gcd(i,j)}\mu(d)\ \ k\in prime \]

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

\[\sum_{k=1}^{n}\sum_{d=1}^{\frac {\min(n,m)}k}\mu(d)\cdot \frac n{kd}\cdot \frac m{kd} \ \ k\in prime \]

但过不了,考虑优化

\(T=kd\),有

\[\sum_{k=1}^{n}\sum_{d=1}^{\frac {\min(n,m)}k}\mu(d)\cdot \frac n{T}\cdot \frac m{T} \ \ k\in prime \]

枚举 \(T\),往前提

\[\sum_{T=1}^{\min(n,m)}\frac nT\cdot \frac mT\sum_{k|T,k\in prime}\mu(\frac Tk) \]

后面一大部分可以预处理

\[\sum_{k|T,k\in prime}\mu(\frac 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\),求

\[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k] \]

思路

和之前几乎一摸一样

\[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(\frac ik,\frac jk)=1] \]

\[\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac mk}[\gcd(i,j)=1] \]

\[\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac mk}\sum_{d|\gcd(i,j)}\mu(d) \]

\[\sum_{d=1}^{\min(n,m)}\mu(d)\cdot \frac n{kd}\cdot\frac m{kd} \]

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

#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

题意

\[\sum_{i=a}^{b}\sum_{j=c}^{d}[\gcd(i,j)=k] \]

思路

不再推一遍了

P3455

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

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

\[\sum_{i=1}^{b}\sum_{j=1}^{d}[\gcd(i,j)=k] \]

的值

那么最终的结果 \(Ans=ans(b,d,k)-ans(c-1,b,k)-ans(a-1,d,k)+ans(a-1,c-1,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\),求

\[\sum_{i=1}^n\sum_{j=1}^md(ij) \]

思路

已知

\[d(i\cdot j)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

so,要求的就是

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

直接枚举 \(i,j\) 的因数

\[\sum_{i=1}^n\sum_{j=1}^m\frac ni\cdot \frac mj[\gcd(i,j)=1] \]

反演一下

\[\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\frac ni\cdot \frac mj\cdot \mu(d) \]

改为枚举 \(d\)

\[\sum_{d=1}^{\min(m,n)}\mu(d)\sum_{i=1}^n\sum_{j=1}^m\frac ni\cdot \frac mj[d|gcd(i,j)] \]

减少枚举次数

\[\sum_{d=1}^{\min(m,n)}\mu(d)\sum_{i=1}^{\frac nd}\sum_{j=1}^\frac md\frac n{di}\cdot \frac m{dj} \]

结合一下

\[\sum_{d=1}^{\min(m,n)}\mu(d)(\sum_{i=1}^{\frac nd}\frac n{di})(\sum_{j=1}^\frac md\cdot \frac m{dj}) \]

接下来预处理一个函数 \(f(x)=\sum_{i=1}^{x}\frac xi\),则

\[ans=\sum_{d=1}^{\min(m,n)}\mu(d)\cdot f(\frac nd)\cdot f(\frac 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;
	}
}
posted @ 2022-07-05 16:26  Into_qwq  阅读(24)  评论(0编辑  收藏  举报