前言
就是被数论虐了...
啊,学渣苦,学渣累——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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#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;
}
貌似愉快地完结了
其实这是一个大坑