莫比乌斯反演——从入门到放弃

前言

就是被数论虐了...

啊,学渣苦,学渣累——Friedrich Taylor

目录

1.mobius函数

2.反演公式

3.超水的模板题

1.莫(meng)比乌斯函数

怕你不知道,其实莫比乌斯函数是这个样子的

μ(n)=δw(n)Ω(n)λ(n);

但实际上这和今天所讲内容并没有什么直接联系

对于高中阶段的信息学而言,更需要的是“能够利用某种事物”,而不是“能够理解某种事物"

所以我们会给出莫比乌斯函数的一些性质。

(1)莫比乌斯函数的值

当n=1 时,μ(n)=1;

当n可以被表示为k个不同质因数乘积时,μ(n)=(-1)^k;

其余情况下μ(n)=0;

(2)莫比乌斯函数是一个积性函数

好像很容易理解对不对

(3) 奇奇怪怪但是非常有用的性质

对于任意正整数n有

Σμ(d)(d满足d整除n)==(n=1) 也就是只有n为1时该式才有值为1,否则为0

Σμ(d)/d(d满足d整除n)=φ(n)/n

2.反演公式

前提:F(n),f(n)是定义在非负整数集合上的两个函数,并且有

F(n)=Σf(d) (d满足d整除n)

则有

f(n)=Σμ(d)*F(n/d)(d同样满足d整除n)

同时还有另一种描述

前提:F(n),f(n)是定义在非负整数集合上的两个函数,并且有

F(n)=Σf(d) (d满足n整除d)

则有

f(n)=Σμ(d)*F(n/d)(d同样满足n整除d)

好像不是很好理解

简单地证明一下 https://baike.baidu.com/item/%E8%8E%AB%E6%AF%94%E4%B9%8C%E6%96%AF%E5%8F%8D%E6%BC%94

你问我为什么甩链接?因为打字太累了(其实是作者比较蠢)

然后我们貌似就可以做题啦,实在是妙!

超水的模板题

为什么我们不说是很水?因为可能会被误会来自河北

既然都说了是水,那么不水不足以鼓舞大家

(1)gcd(bzoj2818)

不是权限题

你让n除以一下要枚举的质数,然后...

等等为什么是欧拉函数啊喂

然后还要处理x,y均为同一个素数的情况所以这个题貌似没有用反演

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define int long long
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
#define stan 1111111
int phi[stan],pri[stan],f[stan],cnt,n;
bool jud[stan];
void preact(){
	phi[1]=1;
	for(int i=2;i<=n;++i){
		if(!jud[i]) pri[++cnt]=i,phi[i]=i-1;
		for(int j=1;j<=cnt&&pri[j]*i<=n;++j){
			jud[i*pri[j]]=true;
			if(i%pri[j]==0){
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}
			phi[i*pri[j]]=phi[i]*phi[pri[j]];
		}
	}
	for(int i=2;i<=n;++i)
		f[i]=f[i-1]+(phi[i]<<1);
	return ;
}
int solve(int n){
	int ret=0;
	for(int j=1;j<=cnt;++j)
		ret+=1+f[n/pri[j]];
	return ret;
}
signed main(){
	n=read();
	preact();
	write(solve(n));
	return 0;
}
(2)YY的gcd

这个是真要反演了

但是等式不如链接好写http://hzwer.com/6142.html

提醒一下此处要用上面提到的变式

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define int long long
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
#define stan 10000011
int pri[stan],mu[stan],f[stan],T,n,m,cnt;
bool exi[stan];
inline void preact(){
	mu[1]=1;
	for(int i=2;i<=10000000;++i){
		if(!exi[i]){
			pri[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&pri[j]*i<=10000000;++j){
			exi[i*pri[j]]=true;
			if(i%pri[j]==0){
				mu[i*pri[j]]=0;
				break;
			}
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=cnt;++i)
        for(int j=pri[i];j<=10000000;j+=pri[i])
            f[j]+=mu[j/pri[i]];
    for(int i=1;i<=10000000;++i)
       f[i]+=f[i-1];
	return ;
}
inline int solve(int n,int m){
	int ret=0;
	if(n>m) swap(n,m);
	for(int i=1,lst;i<=n;i=lst+1){
		lst=min(n/(n/i),m/(m/i));
		ret+=(n/i)*(m/i)*(f[lst]-f[i-1]);
	}
	return ret;
}
signed main(){
	T=read();
	preact();
	while(T--){
		n=read();m=read();
		write(solve(n,m));
		putchar('\n');
	}
	return 0;
}
貌似愉快地完结了 其实这是一个大坑


你可能感兴趣的:(OI)