数学&数论
# 01 快速幂
一种快速求解幂的方法
(貌似是一句废话)
思路是使用二进制求解
example
11 (1011)Bin (1000)Bin (10)Bin (1)Bin
3 = 3 =3 * 3 *3
3 1 0
2 2 2
= 3 + 3 + 3
(蒟蒻作者不会 Latex ,凑合一下吧)
而明显我们可以快速求出 x^(2^k) 的值
another example
-----------------------------------------------------
编号 | res | 级数 | 每次执行平方操作后 res 的值
1 | 3 | 1 | 9
2 | 9 | 2 | 81
3 | 81 | 4 | 81*81
... | ... | 2^k | ...
-----------------------------------------------------
可见只需通过平方就可以求出 x^(2^k)
然后搞一些简简单单的位运算就可以啦
注意快速幂一般都需要取模
(要不然 C++ 的 pow 他不香吗)
Code [P1226 快速幂]
#include<bits/stdc++.h>
using namespace std;
long long p;
long long Qpow(long long base,long long add){
long long cnt=1;
while(add){
if(add&1)cnt=(cnt*base)%p;
base=(base*base)%p;
add>>=1;
}
return cnt;
}
int main(){
long long a,b;
scanf("%lld%lld%lld",&a,&b,&p);
printf("%lld^%lld mod %lld=%lld",a,b,p,Qpow(a,b));
return 0;
}
# 02 质数
## 01 判断质数
判断质数一般用试除法
最暴力的方法显然是从 2 到 n-1 逐个遍历检查
根据小学知识一个数的因数一般成对存在
所以只需遍历到 sqrt(x) 即可
//只需判断 i < x/i 即可
Code
bool IsPrime(int key){
for(int i=2;i<=key/i;i++)if(key%i==0)return false;
return true;
}
## 02 筛质数
### 01 埃氏筛
埃氏筛不讲了,上代码
Code [埃氏筛]
int prime[N],cnt=0;//记录质数的值和数量
bool st[N];//标记数组 为 0 表示是质数
void Ai_Shi_Shai(int lim){
st[1]=true;
for(int i=2;i<=lim;i++){
if(st[i]==false){
prime[++cnt]=i;
for(int j=i+i;j<=n;j++)st[i]=true;
}
}
}
### 02 线性筛
顾名思义时间复杂度 O(n)
思路是只被其最小质因子筛去
我们用每一个数和所有质数相乘进行筛除
一开始能保证 prime[j] 是乘积的最小质因子
直到 i 的最小质因子等于 prime[j] 为止
此时直接 break 就好
Code [P3383 线性筛质数模板]
#include<bits/stdc++.h>
using namespace std;
const int N=1e8+9,M=1e7+9;
int prime[M],cnt;
bool st[N];
void Eular(int n){
st[1]=true;
for(int i=2;i<=n;i++){
if(!st[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
st[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int main(){
int n,T,p;
scanf("%d%d",&n,&T);
Eular(n);
while(T--){
scanf("%d",&p);
printf("%d\n",prime[p]);
}
return 0;
}
# 03 约数,ex欧拉筛与欧拉函数
# 00 前置知识:积性函数
如果一个函数 Shit!(x) 满足
Shit!(a) * Shit!(b) = Shit!(a*b) (GCD(a,b)=1,即 a,b 互质)
则称这个函数 Shit!(x) 为积性函数
# 01 欧拉函数
欧拉函数 phi(n) 指小于等于 n 且与 n 互质的数的个数
欧拉函数性质:
1. phi(1) =1
-> 因为 1 和 1 互质
2. phi(prime)=prime-1
-> 明显从 1 ~ prime 中除 prime 本身任何数都与 prime 互质
3. phi(prime^k)=prime^k-prime^(k-1)
-> 可知 1 ~ prime^k 中 只要不是 prime 的倍数则都与原数互质
而在此范围内 prime 的倍数有 (prime^k)/(prime)=prime^(k-1) 个
所以用 prime^k 减去该数即为结果
4. phi(x) 是积性函数
-> 这个证明过程较为复杂,就不贴了
(没事,写了你就不会在意证明了)
p[1] p[2] p[k]
5. 若 N= a[1] * a[2] * ... * a[k]
(a[i] 为质数,p[i] 为正整数)
则 phi(N)=N(1-1/a[1])(1-1/a[2])...(1-1/a[k])
//实际上是 phi 函数的解析式
-> 使用了性质三和四
由性质四
phi(N)=phi(a[1]^p[1])*phi(a[2]^p[2])* ... *phi(a[k]*p[k])
然后把性质三带入
phi(N)=(a[1]^p[1]-a[1]*(p[1]-1))*(a[2]^p[2]-a[2]^(p[2]-1))* ... *(a[k]^p[k]-a[k]^(p[k]-1))
=a[1]^p[1](1-1/a[1])* ... *a[k]^p[k](1-1/a[k])
=(a[1]^p[1])*(a[2]^p[2])* ... *(a[k]^p[k])*(1-1/a[1])*(1-1/a[2])* ... *(1-1/a[k])
最终将 N 的唯一分解定理代入
phi(N)=N(1-1/a[1])* ... *(1-1/a[k])
QED!
然而如果要求出 1~n 中每一个数的欧拉函数
(那阁下又将如何应对呢)(我再也不玩抽象了)
我们发现这个东西欧拉筛可以干
当然根据欧拉筛的思路
我们主要使用已有的 phi 去得到其他数的 phi 值
分成三种情况
1. k 是质数
-> phi(k)=k-1
2. GCD(k,prime) = 1
-> 由积性函数性质
phi(k*prime)=phi(k)*phi(prime)=(prime-1)*phi(k)
3. GCD(k,prime) = prime
-> 说明 k 已有 prime 这个因数
看上面的唯一分解定理只有其中的 N 会有所改变
所以 phi(k*prime)=phi(k)*prime
Code [ACP10071 筛法求欧拉函数]
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int prime[N],phi[N],cnt;
bool st[N];
void Eular_Get_Phi(int Lim){
st[1]=1,phi[1]=1;
for(int i=2;i<=Lim;i++){
if(st[i]==0){
phi[i]=i-1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&(long long)prime[j]*i<=(long long)Lim;j++){
st[i*prime[j]]=true;
if(i%prime[j]==0){
phi[i*prime[j]]=prime[j]*phi[i];
break;
}else{
phi[i*prime[j]]=(prime[j]-1)*phi[i];
}
}
}
}
int main(){
int n;
scanf("%d",&n);
Eular_Get_Phi(n);
long long ans=0;
for(int i=1;i<=n;i++)ans+=phi[i];
printf("%lld\n",ans);
return 0;
}
# 02 约数个数函数
用 d(x) 表示
顾名思义这个函数指的是 x 的约数的个数
解析式如下:
p[1] p[2] p[k]
若 N= a[1] * a[2] * ... * a[k]
则 d(N)=(p[1]+1)*(p[2]+1)* ... *(p[k]+1)
这个函数其实比较简单
显然每个质因数 a[i] 都可以选择 0~k[i] 的次数
乘法原理即可得出原式
(以下正片开始)
显然该函数为积性函数
(这就不需要玄学了,如果 a,b 两个数互质则他们的乘积在唯一分解定理中不会有重叠,稍加思考即可得出)
发现一件事情
很多积性函数都可以用欧拉筛求解
仍然分成三种情况
1. k 是质数
-> D(k)=2
2. GCD(k,prime) = 1
-> 由积性函数性质
D(k*prime)=D(k)*D(prime)=2*D(k)
3. GCD(k,prime) = prime
-> 说明 prime 是 k 的最小质因子 a[i]
显然 D(k*prime)=(p[1]+1)*(p[2]+1)* ... *(p[i]+1+1)* ... *(p[k]+1)
所以我们还需要维护每个数最小质因子的次数 MinRoot
仍然分成三种情况
1. k 是质数
-> MinRoot(k)=1
2. GCD(k,prime) = 1
-> prime 是 k 唯一的最小质因子 a[i]
MinRoot(k*prime)=1
3. GCD(k,prime) = prime
-> 说明 prime 是 k 的最小质因子 a[i]
所以会将此次数 +1
即 MinRoot(k*prime)=MinRoot(k)+1
QED!
Code [P1403 约数研究]
//好冷门的题,还是道橙题
//是,我不是正解
//可是你能找到例题吗
//拉倒吧我园子没油了
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
bool st[N];
int prime[N],cnt,MinRoot[N],D[N];
void Eular(int Lim){
st[1]=1,D[1]=1;
for(int i=2;i<=Lim;i++){
if(!st[i]){
prime[++cnt]=i;
MinRoot[i]=1;
D[i]=2;
}
for(int j=1;j<=cnt&&(long long)i*prime[j]<=(long long)Lim;j++){
st[i*prime[j]]=true;
if(i%prime[j]==0){
MinRoot[i*prime[j]]=MinRoot[i]+1;
D[i*prime[j]]=D[i]/(MinRoot[i]+1)*(MinRoot[i]+2);
break;
}else{
MinRoot[i*prime[j]]=1;
D[i*prime[j]]=2*D[i];
}
}
}
}
int main(){
int n;
scanf("%d",&n);
Eular(n);
long long ans=0;
for(int i=1;i<=n;i++)ans+=D[i];
printf("%lld\n",ans);
return 0;
}