蒟蒻的数论笔记
数论笔记
定义
一些规定
1.如无特殊标记,
2.如无特殊说明,
3.按照语境通常是向下取整的含义
4.表示容易质数,表示质数集,如无特殊说明,表示第大的质数
许多数论函数
欧拉函数:[1,n]中与n互质的数的个数
刘维尔函数
莫比乌斯函数
约数个数函数
元函数
恒等函数
单位函数
约数和函数
积性函数:
完全积性函数:
狄利克雷卷积
定义
设为积性函数
则
狄利克雷卷积的一些性质
定理
小知识
常见结论
神秘小知识
(1)
证明如下:
构造一个二元数列使得,特殊地,也被认为是合法的
构造另一个二元数列其中,
对于考虑其到的映射,
不难发现
可以得到
再考虑到的映射
可以发现
放缩得
显然有,所以
结合定义知,即
得证
(2)
证明如下
若
由的定义知,若
故而我们只考虑的,下面的都满足这一性质
设
就是说考虑有个质因子的的总贡献,表示这样的的个数,特殊地,我们有,也就是的贡献
由组合数的知识容易得到
考虑用来表达,简单带入可得
考虑构造函数
所以有
综上,得证
(3)
也就是说欧拉函数是积性函数,但不是完全积性函数
(4)
杜教筛的构造
要求的前缀和,构造一个很好求前缀和的函数和一个很好求前缀和的辅助函数,使得
按狄利克雷卷积的定义展开
考虑用表示,于是考虑交换求和符号,使得和分离:
发现,所以我们把右边第一项提出来:
整理得
由于构造的是容易求得的,即可以在低于线性的时间内求出(实际上很多时候都是的),
也显然可以在的时间里求出,所以只需要在低于线性的时间里求的除去第一项的前缀和
考虑使用整除分块,则转化为求在不超过段上的和,前缀和即可,
对于项,我们直接递归,暴力地预处理出个前缀和即可
复杂度分析
下文考虑中均认为求的前缀和是的
运用杜教筛,我们把求转化为了求所有的,本质上只有种取值,忽略常数后,我们设杜教筛的复杂度为,可得方程
含义为,求前n项前缀和的复杂度是整除分块的复杂度+处理出每个和的复杂度
这里我们默认了超出了预处理的前缀和,但显然可能是被预处理过的值,所以需要考虑预处理,设预处理了个,那么总的复杂度为:
注意到所有都已经被预处理了,可以得到,复杂度为,
接着考虑的处理,考虑继续展开,注意此时不需要再预处理了:
上式最后的含义为一个大于的值迭代到以内的迭代次数
令总复杂度为,选为主元,考虑其极值,得
最终的复杂度为.
(5)
莫比乌斯反演
证明如下
得证
积性函数
(6)
对于任意积性函数有
有算数基本定理和积性函数的定义容易推出
(7)对于任意完全积性函数有
若积性函数满足上式,则也是完全积性函数
(8)
积性函数的神秘性质
由算数基本定理容易推出
(9)
由结论1得:
考虑消去其中的,利用结论2,两边同卷:
Powerful Number 筛
个人觉得比上面两种筛法都简单
先定义powerful number为
说人话就是所有质因子次数不小于2的数
还是求积性函数的前缀和,构造函数,并使得的前缀和容易求出
令
有
考虑展开卷积,得
考虑的前缀和是容易求出的,交换求和号
这依然是的,但因为是积性函数并且,所以
考虑的数量,以下的是
由定义知
上文的仅表示的质因子
不难发现
那么
记,那么powerful number的数目就是的前缀和
考虑枚举,计算对于的贡献,为了不算重,应该只枚举,那么
那么就只需要求出中的所有powerful number,然后就可以求出的前缀和,其中是求的前缀和的复杂度
接下来介绍一个小技巧:
观察,发现,其中
所以只要构造出就可以求出,减小了构造的工作量
powerful number筛的时间复杂度和思维难度都有一定优势,但其辅助函数难以构造,使得很多问题不能用其解决
真定理
1.欧拉定理:
特殊地,时,有
注意,使用时应当判断与的大小
2.费马小定理
其实就是欧拉函数的性质在欧拉定理中的应用:
代码
封装好了一些基本的东西
卢卡斯定理没有过
class MyMath
{
public:
vector<int> prime;
vector<int> phi;
vector<int> mu;
template<typename Tp_> inline Tp_ inv(Tp_ x,Tp_ p) {return pow(x,-1,p);}
inline bool is_prime(ll x)
{
if(x<0) x=-x;
if(!prime.empty())
if((*prime.rbegin())*(*prime.rbegin())>=x)
{
for(vector<int>::iterator it=prime.begin();(*it)*(*it)<=x&&it!=prime.end();++it)
if(!(x%(*it))) return false;
return true;
}//surely a bit faster,almost log(n)
for(ll i(2);i*i<=x;++i)
if(!(x%i)) return false;
return true;
}//n^0.5 hardly used
inline ll pow(ll a,ll x,ll p)
{
if(x==0) return 1;
if(x>0)
{
ll ret=1;
while(x) ret=(x&1?a*ret%p:ret),x>>=1,a=a*a%p;
return ret;
}
else
{
if(!phi.empty())
if(phi.size()>p)
return pow(a,x%phi[p]+phi[p],p);
ll ph=Phi(p);
return pow(a,x%ph+ph,p);
}
}//maybe log,n^0.5 when using Phi()
template <typename Tp_> inline Tp_ gcd(Tp_ x,Tp_ y)
{
if(x>y) swap(x,y);
if(x==0) return (y?y:1);
return gcd(y%x,x);
}//almost log
template <typename Tp_> inline Tp_ lcm(Tp_ x,Tp_ y) {return x/gcd(x,y)*y;}
inline int ex_gcd(int a,int b,int c,int &p,int &q)
{
int g=ExEuclid(a,b,p,q);
if(c%g) return p=0,q=0;
g=c/g,p*=g,q*=g;
return 1;
}//a*p+b*q=c(mod p),about log,maybe
template<typename Tp_> inline Tp_ Phi(Tp_ x)
{
Tp_ tmp=x,ret=x;
for (Tp_ i=2;i*i<=x;++i)
if (tmp%i==0)
{
ret=ret-ret/i;
while (tmp%i==0) tmp/=i;
}
if(tmp>1) ret-=ret/tmp;
return ret;
}//n^0.5
template<typename Tp_> inline Tp_ Lucas(Tp_ n,Tp_ m,Tp_ p)
{
if(m==0) return 1;
Tp_ np=n%p,mp=m%p;
if(np<mp) return 0;
mp=min(np-mp,mp);
Tp_ p1=1,p2=1;
for( Tp_ i = 0 ; i < mp ; ++i )
p1=p1*(n-i)%p,
p2=p2*(i+1)%p;
return (p1*pow(p2,p-2)%p)*Lucas(n/p,m/p,p)%p;
}//p must be a prime
inline void sieve(const int capN)
{
bool *isp;
isp=new bool [capN+5];
memset(isp,0,sizeof(bool)*(capN+4));
if(!prime.empty()) prime.clear();
for(int i(2);i<=capN;++i)
{
if(!isp[i]) prime.emplace_back(i);
for(vector<int>::iterator it=prime.begin();it!=prime.end();++it)
{
if(i*(*it)>capN) break;
isp[i*(*it)]=1;
if(!(i%(*it))) break;
}
}
delete isp;
}//O(n) and need more place
inline void phi_sieve(const int capN)
{
if(!prime.empty()) prime.clear();
phi.resize(capN+4,0),phi[1]=1;
for(int i(2);i<=capN;++i)
{
if(!phi[i])
prime.emplace_back(i),
phi[i]=i-1;
for(vector<int>::iterator it=prime.begin();it!=prime.end();++it)
{
if(i*(*it)>capN) break;
if(!(i%(*it)))
{
phi[i*(*it)]=phi[i]*(*it);
break;
}
else phi[i*(*it)]=phi[i]*(*it-1);
}
}
}//cna get phi while finding primes in [1,capN]
private:
inline int ExEuclid(int a,int b,int &p,int &q)
{
if(b==0) return p=1,q=0,a;
int d=ExEuclid(b,a%b,p,q);
int tmp=p;
p=q,q=tmp-a/b*q;
return d;
}//a help function of ex_gcd
}M;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现