一、
模运算
当答案或者运算过程中数据太大,题目要求输出答案对某个大数据取模,
有以下结论:
(a+b)%MOD=(a%MOD+b%MOD)%MOD
(a-b)%MOD=(a%MOD-b%MOD)%MOD
(a*b)%MOD=(a%MOD*b%MOD)%MOD
而除法取模需要逆元,后面会介绍
c++语言取模过程中,会遵循商尽量大的原则,所以(-5)%3=-2
这个不符合数学上取模的标准,因此使用c++对减法取模应该写成:
(a-b+MOD)%MOD
二、质数
1~n当中,大约有 n l n ( n ) \frac{n}{ln(n)} ln(n)n个质数
1、质数筛
埃氏筛
复杂度为O(nlg(lg(n)))
const int maxn=1e6;
int prime[maxn];
bool vis[maxn];
int cnt=0;
void Prime(int n){
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
for(int j=i*i;j<=n;j+=i){
vis[j]=true;
}
}
}
}
//此处有个小优化,嵌套的循环内 j j j从 i ∗ i i * i i∗i 开始,而不是 i + i i + i i+i开始,因为 i ∗ ( 2 − > i − 1 ) i*(2 ->i-1) i∗(2−>i−1)在这之前都已经被筛去。
欧拉筛
复杂度O(n)
const int maxn=1e6;
int prime[maxn];
bool vis[maxn];
int cnt=0;
void Prime(int n){
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++cnt]=i;
}
for(int j=1;prime[j]*i<=n;j++){
vis[prime[j]*i]=true;
if(i%prime[j]==0){
//保证每个合数都用该数最小的质因子筛掉
//当i%prime[j]==0的时候,说明如果j继续加一,被筛掉的数就不是用其最小的质因子筛去的了。
break;
}
}
}
}
2、质因数分解----试除法
任意正整数
n
n
n最多有一个比
s
q
r
t
(
n
)
sqrt(n)
sqrt(n)大的质因子,质因数分解模版如下:
(⁎⁍̴̛ᴗ⁍̴̛⁎)
质因数分解模版
int pdivid[10000];
int cnt=0;//记录一共有多少个质因子
void divid(int n){
for(int i=2;i<=n/i;i++){
if(n%i==0){
//此处i一定为质数
pdivid[++cnt]=i;
int s=0;
while(n%i==0){
n/=i;
s++;//可以计算每个质因子出现的次数,这份代码不记录这个数
}
}
}
if(n>1){
pdivid[++cnt]=n;
}
}
int main(){
int n;
cin>>n;
divid(n);
cout<<pdivid[cnt]<<endl;
}
三、约数
1、试除法
直接枚举从1~sqrt(n)所有的数字,可以得到n所有的约数。
每个数的约数个数期望为lg(n).
2、约数个数和约数之和
先对N进行质因数分解,假设分解后:
o(`ω´ )o:::::
N = = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p k c k N== p_1^{c_1} * p_2^{c_2} * ... *p_k^{c_k} N==p1c1∗p2c2∗...∗pkck
(pi为质因子,ci为质因子对应的次数)
那么N的约数个数为::
(
c
1
+
1
)
∗
(
c
2
+
1
)
∗
.
.
.
∗
(
c
k
+
1
)
(c1 + 1) * (c2 + 1) * ... * (ck + 1)
(c1+1)∗(c2+1)∗...∗(ck+1)
N的约数之和为::
(
p
1
0
+
p
1
1
+
.
.
.
+
p
1
c
1
)
∗
.
.
.
∗
(
p
k
0
+
p
k
1
+
.
.
.
+
p
k
c
k
)
(p_1^0 + p_1^1 + ... + p_1^{c_1}) * ... * (pk^0 + pk^1 + ... + pk^{c_k})
(p10+p11+...+p1c1)∗...∗(pk0+pk1+...+pkck)
(把上式子展开后,直接看出为计算每个约数的和)
const int mod=1e9+7;
int main(){
int n;
cin>>n;
unordered_map<int,int> primcnt;
while(n--){
int x;
cin>>x;
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
primcnt[i]++;
}
}
if(x!=1){
primcnt[x]++;
}
}
ll ans=1;
//遍历map
for(auto prim:primcnt){
ans=(ans*(prim.second+1))%mod;
}
cout<<ans<<endl;
}
3、最大公约数(gcd)
欧几里得算法
g c d ( a , b ) = g c d ( b , a gcd(a,b)=gcd(b,a%b) gcd(a,b)=gcd(b,a
(好神奇啊!)ʕ •ᴥ•ʔ
int gcd(int a,int b){
if(b==0){
return a;
}
else
return gcd(b,a%b);
}
四、欧拉函数
1、定义
欧拉函数是小于n的正整数中与n互质的数的数目(比如
φ
(
1
)
=
1
φ(1)=1
φ(1)=1)
2、公式
φ
(
N
)
=
N
∗
(
1
−
1
p
1
)
∗
.
.
.
.
.
∗
(
1
−
1
p
i
)
φ(N)=N*(1-\frac{1}{p_1}) * ..... * (1-\frac{1}{p_i})
φ(N)=N∗(1−p11)∗.....∗(1−pi1)
其中
p
i
p_i
pi为
N
N
N的质因子。
3、筛法求1~n的欧拉函数
void get_eulers(int n){
//得到1~n所有欧拉函数的值
//其实是欧拉筛变体
int cnt=0;
phi[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prim[++cnt]=i;
//如果这个数是个素数,显然在1~i之间,有i-1个数与其互质
phi[i]=i-1;
}
for(int j=1;prim[j]*i<=n&&j<=cnt;j++){
vis[i*prim[j]]=1;
if(i%prim[j]==0){
//如果prim[j]是i的因子
//根据公式,phi[i]=i*(1-1/P1)*...*(1-1/Pi)
//i*prim[j]的所有因子跟i相同
//phi[i*prim[i]]=prim[i]*i*(1-1/P1)*...*(1-1/Pi)=phi[i]*prim[i]
phi[i*prim[j]]=phi[i]*prim[j];
break;
}
//如果prim[j]不是i的因子
//那么i*prim[j]的因子与i相差一个prim[j]
//phi[i*prim[i]]=phi[i]*prim[j]*(1-1/prim[j])=phi[i]*phi[prim[j]]
phi[i*prim[j]]=phi[i]*(prim[j]-1);
}
}
}
欧拉定理
如果a与n互质,那么 a p h i ( n ) ≡ 1 ( m o d n ) a^{phi(n)}≡1(mod n) aphi(n)≡1(modn).
四、快速幂
ll my_pow(int a,int k,int p=1e9+7){
ll res=1;
while(k){
if(k&1){
//如果k不能整除2
res=(res*a)%p;
}
k>>=1;
a=(a*a)%p;//让底数平方
}
return res;
}