φ 这玩意儿数学课上好像见过
(一) φ 是个啥玩意儿?
欧拉函数——
φ(n)
表示
[1..n]
中与
n
互质的数的个数,它是一个不完全积性函数,即对于互质的
(二) φ 有一大坨可说的东西
关于 φ ,首先有三个基本的引理:
- 对于任何一个质数
p
,
φ(p)=p−1 。这个比较显然,由质数的定义即可得到。 - 对于任何一个质数
p
的幂
pa , φ(p)=(p−1)∗pa−1 。
这是因为既然 p 是个质数,那么在[1,pa] 的范围内只有p的倍数与 pa 不互质。而在 [1,n] 内i的倍数的个数为 ⌊ni⌋ ,则在 [1,pa] 范围内p的倍数有 pa−1 个。一共有 pa 个数,减去不互质的那些剩下的就有 (p−1)∗pa−1 个啦。 - 对于任意两个互质的数
a,b
,
φ(a∗b)=φ(a)∗φ(b)
ATP也是看了人家的证明才明白的啦。。这里就贴个链接吧。
根据上面提到的引理2,我们可以得到
φ
的通项公式:
设
n
的完全分解式为
证明:由于每个素数的幂都是互质的,那么 φ(n)=φ(pa11)∗φ(pa22)∗...∗φ(pann) 。
而根据引理2,上面的柿子可以转化成
将上面柿子的小括号内提出一个 pi ,就有
根据乘法交换律,我们可以把那一坨 paii 提出来,可以发现它们的乘积恰好是 n ,则
φ 函数还有一个关于莫比乌斯反演的公式:
然而愚蠢的ATP并不会证明。
(三)如何求 φ ?
求
φ
有两种常用的方法:线性筛[1..n]之间的
φ
和
O(n√)
地求单个
φ
。
线性筛的方法放到下面去说啦。这里就提一句如何
O(n√)
地求。
其实看到那个
φ
的通式,我们就已经可以有一个科学的想法了——将n分解质因数,利用通式求
φ
。为了防止做除法的时候出现小数之类的东西,我们可以一个一个把括号展开。例如第一个括号展开就变成了
(n−np1)∗(1−1p2)∗...∗(1−1pn)
,第一项就变成了一个整数,然后每次求的时候再展开一项就可以了。
这里贴一个代码:
long long calc_phi(long long d){
long long sum=d;
for (long long i=2;i*i<=d;i++)//注意for循环的上限
if (d%i==0){//不要漏了这个条件
sum=sum*(i-1)/i;
while (d%i==0)
d/=i;//分解质因数
}
if (d>1) sum=sum*(d-1)/d;//最后可能还剩下一个大质数
return sum;
}
BZOJ2705 longge的问题和HDU2588 GCD就是 O(n√) 求 φ 的板子题啦。
μ ——动摩擦因数?
(一) μ 又是什么?
莫比乌斯函数—— μ(n) ,与n的质因子情况有关。具体地:
它也是一个不完全积性函数,证明根据定义即可得到。
(二) μ 是不是可以用来画柿子呀
看到
μ
的名字就觉得它跟莫比乌斯反演有千丝万缕的联系对吧。。。
反演中最常用的柿子之一——
同样,愚蠢的ATP不会证明。。。。。。
实际上 μ 可以理解成一个容斥系数。
(三) μ 也可以科学的求出来!
同样,线性筛 μ 的方法放在下面说。
显然我们可以把
n
分解质因数然后在
但是假如我们在做莫比乌斯反演的时候遇到了上面那种带着
μ
的柿子,并且
n
又很大没有办法直接筛,我们还有一种方法在
这里贴一个代码:
for (int i=1;i<=s;i++){
d[1<<(i-1)]=div[i];
mu[1<<(i-1)]=-1;
}
for (int i=1;i<(1<<s);i++)
if (i!=0){
int lowbit=(i&(-i));
mu[i]=-mu[i^lowbit];
d[i]=d[i^lowbit]*d[lowbit];
}
d和mu数组的下标都是二进制压位。显然我们只需要求那些由单个质因子组成的因数的mu,因为如果有平方因子的话它就是0,对计算结果没有什么贡献。于是用d[S]存储因数状态为S的时候的因数,div数组存储n的第i个因数,mu[S]存储因数状态为S的时候的 μ 值。
lowbit是当前i的最后一个1的权值,和树状数组的那个是一样的,实际上相当于一个递推,每次用最小的质因子来求当前的mu。
科学的筛法——线性筛
线性筛可以在
O(n)
的时间内求出[1..n]范围内的所有目标函数值。要求目标函数必须是一个积性函数。并且因为保证每个数字都只会被它的最小质因子筛去一次,所以保证了科学的时间复杂度。
线性筛求函数值通常分为三种情况来讨论:质数;最小质因子指数为1;最小质因子指数不为1。
其中第三种情况是最复杂的,不同的函数处理的方法是不同的。第一种情况可以根据定义来求,第二种情况可以根据积性函数的性质来求。
(一)我可以筛欧拉函数—— φ !
首先根据上面提
φ
的时候提到的引理1,当
n
为质数的时候
关键是当最小质因子指数不为1的时候,我们有一个结论:
φ(s∗p)=φ(s)∗p(s%p=0)
。
证明:设
s=q∗pn
,
q和pn
互质。则
φ(s)=φ(q)∗φ(pn)
,
φ(s∗p)=φ(q)∗φ(pn+1)
。
根据上面提到关于
φ
的引理2
于是
这里贴一个代码:
p[1]=1;
for (int i=2;i<=n-1;i++){
if (ext[i]==false){
++t;prm[t]=i;
p[i]=i-1;
}
for (int j=1;j<=t;j++){
if (i*prm[j]>n) break;
ext[i*prm[j]]=true;//筛去合数
if (i%prm[j]==0){
p[i*prm[j]]=p[i]*prm[j];
break;//一定要break防止同一个数的重复计算
}//即保证每个数只被它的最小质因子筛去一次
else
p[i*prm[j]]=p[i]*(prm[j]-1);
}
}
(二)我可以筛莫比乌斯函数—— μ !
μ
的情况比较简单,当最小质因子指数不为1的时候
μ(n)=0
,其他情况显然(喂喂明显是你懒得打这么多字了吧)。
仍然贴代码:
mu[1]=1;
for (int i=2;i<=N;i++){
if (ext[i]==false){
prm[++prm[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prm[0];j++){
if (i*prm[j]>N) break;
ext[i*prm[j]]=true;
if (i%prm[j]==0){
mu[i*prm[j]]=0;
break;
}else
mu[i*prm[j]]=-mu[i];
}
}
(三)我可以筛约数个数—— d !
只要记录一下最小质因子的指数
e
,就可以处理最小质因子指数不为1的情况啦。先把最小质因子原来的贡献即
for (int i=2;i<=n;i++){
if (ext[i]==false){
d[i]=2;e[i]=1;//质数的约数个数为2,最小质因子指数为1
prm[++prm[0]]=i;
}
for (int j=1;j<=prm[0];++j){
if (i*prm[j]>n) break;
ext[i*prm[j]]=true;//不要忘了筛去合数
if (i%prm[j]==0){
d[i*prm[j]]=d[i]/(e[i]+1)*(e[i]+2);//在式子里面更新最小质因子的指数
e[i*prm[j]]=e[i]+1;
break;
}
else{
d[i*prm[j]]=d[i]*d[prm[j]];
e[i*prm[j]]=1;
}
}
}
BZOJ1968 约数研究是一个板子题啦。
一坨乱七八糟的东西
(一) φ !1-n中与n互质的数字
1-n中与n互质的数字之和为 n∗φ(n)2 。
证明:首先我们需要知道“
x
与
当 x 与
n−x 不互质的时候,设它们的最大公因数为 g ,那么x=k∗g , n−x=k′∗g ,则 n=(k+k′)∗g ,显然 n 与x 不互质。
那么所有与n互质的数字一定可以两两配对使它们的和为
n
,显然一共有
(二)不要小数!关于取整函数
这里只提一个小问题—— ⌊⌊ni⌋j⌋=⌊ni∗j⌋ 。
肯定会有人问这不是很显然你还提个毛线qwq可是愚蠢的ATP当时做题的时候就是担心万一它不对怎么办啊然后就趴在那里证了半天最终还是证出来了放在这里就当纪念一下叭= =反正也是很愚蠢的证明啦。。
设 ⌊⌊ni⌋j⌋=k ,则 ⌊ni⌋=k∗j+r1(r1<j) ,进一步地, n=i∗(k∗j+r1)+r2(r2<i)=i∗j∗k+r1∗i+r2 。
设 ⌊ni∗j⌋=k′ ,则 n=k′∗i∗j+r′(r′<i∗j) 。
因为 r1<j,r2<i ,所以 r1∗i+r2<r1∗i+i≤i∗j 。
这相当于把
n
对
(三)超级常用!费马小定理和欧拉定理
费马小定理是和平常所用的逆元密切相关的。。并且好像各种地方都会见到它。。它就是说对于任意一个质数p, ap−1≡1(mod p) 。然而需要注意的是它对所有质数都成立,但对某些合数也成立。
利用费马小定理,我们可以发现 a∗ap−2≡1(mod p) ,也就是说,当 p 是质数的时候,一个数在模p意义下的逆元就是它的p-2次方,这样就可以用快速幂代替逆元啦。
费马小定理更一般的形式是欧拉定理:
ATP然而并不会证明。。