欧拉函数
我真的觉得欧拉无所不能…
在提到欧拉函数之前,我们先要说一个定义:积性函数。什么是积性函数?对于互质的数a,b,有f(ab)=f(a)*f(b)。
这个玩意真的特别神奇。最简单的积性函数是τ(n),这个函数表示所有因子数量。例如,τ(6)=4,τ(2)=2,τ(3)=2,τ(6)=τ(2)∗τ(2)。
另一个积性函数是σ(n)表示所有因子之和。σ(6)=12,σ(2)=3,σ(3)=4,σ(6)=σ(2)∗σ(3)
欧拉函数是什么呢?我们称1~n中与n互质的数的个数为欧拉函数,记作ϕ(n)。作为积性函数,我们还是用6来验证一下:ϕ(6)=2,ϕ(2)=1,ϕ(3)=2,ϕ(6)=ϕ(2)∗ϕ(3)。是不是觉得非常神奇?
那么如何求欧拉函数值?有这样一个公式:
例如,160=25⋅5,所以ϕ(160)=160⋅12⋅45=64。那这个东西怎么得来的呢?
以下内容大可无视
来考虑假设p是a的一个因数
那么ap就是1~a中所有的p的倍数的个数
因此不妨来考虑一下这样一个概率问题:从1~a中选出一个数与a互质
这就是一个古典概型,假定a=pk11⋅pk22⋅...⋅pknn,那么显然p1,p2,...,pn的倍数都是与a不互质的。
因此,这就是一个分步的乘法原理:拿到非pi的倍数的概率为1−1pi,结果相乘。
所以,拿到与p互质的数的概率就是∏(1−1pi)
因此,ϕ(n)=n⋅∏(1−1pi),其中pi表示n的一个质因数
这样我们可以在O(n)的时间内求出某个数的欧拉函数值。如果用一些高科技手段分解质因数,又会略微快一点。
但是一般来说如果要涉及到欧拉函数都是多组的,那么这样求出一个欧拉函数表复杂度要O(n2)。这是我们不能容忍的,所以我们可以在欧拉筛里加几句话来处理出一张欧拉函数表。
首先,我们需要了解几个东西。
(1)对素数来说,ϕ(n)=n−1
(2)如果p为质数,i mod p≠0,那么
ϕ(i⋅p)=ϕ(i)⋅ϕ(p)=ϕ(i)⋅(p−1)
(3)如果p为质数,i mod p=0,那么ϕ(i⋅p)=p⋅ϕ(i)
前两条都是显然的,一个是素数本身的性质,一个是积性函数的定义。第三条比较有趣,我们来看一下之前得到的公式:
ϕ(i)=i⋅∏(1−1pj),其中pj表示i的一个质因数,观察一下就会发现,这里由于p为i的质因数,所以∏(1−1pi)这一部分并未发生改变,亦即ϕ(n⋅p)=p⋅n⋅∏(1−1pi)=p⋅ϕ(n).
那么接下来就是欧拉函数与欧拉筛相结合。代码:
#include <iostream>
#define MAXN 40003
using namespace std;
int n;
int phi[MAXN],prime[MAXN],cnt,ans;
bool notprime[MAXN];
void getphi(){
int i,j;
phi[1]=1;
for(i=2;i<=40000;++i){
if(!notprime[i]){
prime[++cnt]=i;
phi[i]=i-1;
}
for(j=1;j<=cnt;++j){
if(i*prime[j]>0) break;
notprime[i*prime[j]]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}else{
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
}
int main(){
getphi();
return 0;
}
非常有趣吧。
北上广深 拔山盖世 BSGS算法
北上广深,啊不对,BSGS算法是Baby-step-Giant-step的简称,从名字上看就是挺蛋疼的一个算法。BSGS算法的用处在于,求解Ax≡B(mod C)其中C为素数。不过相对应的还有EXBSGS用于解C不为素数的情况,那就比较复杂了我们不谈它。
因为一般而言NOIP考数论都会放在Day2的T1,所以出这玩意概率并不大……总之了解一下吧。
Ax≡B(mod C)
设m=⌈m−−√ ⌉,x=i⋅m+j
从而,(Ai)m⋅Aj≡B(mod C)
变一下形,Aj≡B(Ai)m(mod C)
是不是看到了什么熟悉的东西?没错,exgcd求逆元。
我们先将Aj存在Hash表中,然后枚举i,快速的查出结果。
将Aj存表我们称为Baby Step,而枚举i我们称为Giant Step.
代码我就不抄了……各位自己去搜吧,主要把思想了解一下就可以了qwq
那么数论专题基本上没剩什么了,接下来大概是处理一下矩阵快速幂优化求某一类数列的值以及高斯消元法求解线性方程组,还有Catalan数的相关知识。虽然不能算得上是数论,不过放到一块算了。