·最大公约数 gcd
辗转相除法 gcd(a,b)=gcd(b,a%b)
1 int gcd(int x,int y){
2 if(y==0) return x;
3 return gcd(y,x%y);
4 }
效率O(logn)
·最小公倍数 lcm
可由最大公约数推来 lcm(a,b)=a*b/gcd(a,b)
1 int lcm(int x,int y){
2 int p=gcd(x,y);
3 return a*b/p;
4 }
效率O(logn)
·扩展欧几里得 extgcd
求ax+by=gcd(a,b)的整数对(x,y)
也可由gcd推过来
推导过程:
ax+by=gcd(a,b)=gcd(b,a%b)
假设求出 bx'+(a%b)y'=gcd(b,a%b)
那么整理可得 bx'+(a-(a/b)*b)y'=gcd(b,a%b)
ay'+b(x'-(a/b)*y')=gcd(b,a%b)=gcd(a,b)
故 x=y' y=x'-(a/b)*y'
1 int extgcd(int a,int b,int &x,int &y){ //返回值为gcd(a,b)
2 if(b==0) {
3 x=1;y=0;
4 return a;
5 }
6 int d=extgcd(b,a%b,y,x);
7 y-=(a/b)*x;
8 return d;
9 }
可用于求同余方程、逆元
效率O(logn)
·素数筛
线性筛法,很好理解
由于每个合数都只会被筛掉一次,复杂度O(n)
1 void Get_Prime(int n){
2 p[0]=p[1]=0;
3 cnt=0;
4 for(int i=2;i<=n;i++) p[i]=1; //先标记2~n都为素数
5 for(int i=2;i<=n;i++){
6 if(p[i]) prime[++cnt]=i; //i为素数
7 for(int j=1;j<=cnt && (long long)i*prime[j]<=n;j++){
8 p[i*prime[j]]=1; //每个合数都只被自己最小质因子筛掉
9 if(i%prime[j]==0) break;
10 }
11 }
12 }
·欧拉函数 phi
求小于n与n互素的数的个数
phi[i]=i*(1-1/p1)*(1-1/p2)*(1-1/p3)…… 其中p1,p2,p3为i的质因数
可以在线性筛素数的同时求,复杂度O(n)
1 void get_phi(){
2 p[0]=p[1]=0;cnt=0;
3 for(int i=2;i<=n;i++) p[i]=1;
4 for(int i=2;i<=n;i++){
5 if(p[i]==1) phi[i]=i-1,prime[++cnt]=i;
6 for(int j=1;j<=cnt && (ll)i*prime[j]<=n;j++){
7 p[i*prime[j]]=0;
8 if(i%prime[j]==0) {
9 phi[i*prime[j]]=phi[i]*prime[j];
10 break;
11 }
12 phi[i*prime[j]]=phi[i]*(prime[j]-1);
13 }
14 }
15 }
·快速幂
可以把幂想成一个二进制数来理解
1 int Power_Mod(int x,int y){ //求x的y次方
2 int ret=1;
3 while(y){
4 if(y&1) ret*=x;
5 x=x*x;
6 y>>=1;
7 }
8 return ret;
9 }
效率O(logn)
·排列组合
1)加法原理:做一件事有n类做法,第n类有m[n]种做法,总做法数为m[1]+m[2]+...+m[n]
2)乘法原理:做一件事有n个步骤,第n个步骤有m[n]中做法,总做法数为m[1]*m[2]*...*m[n]
乘法原理可以说是加法原理的特殊情况
3)容斥原理 **这很重要**
例如:求gcd(1~m,1~n)=k的数对有多少
设满足条件的数对有f(k)个
则f(k)=(m/k)*(n/k)-f(2*k)-f(3*k)-f(4*k)-……从后往前递推计算即可
4)排列:A(m,n)=m!/(m-n)! (m>n)
5)组合:C(m,n)=m!/((m-n)!*n!) (m>n)
如何求组合数?
法一:C(m,n)=C(m,n-1)*(m-n+1)/n
法二:杨辉三角 C(m,n)=C(m-1,n)+C(m-1,n-1)
·概率与数学期望
1)概率:P(A)=m/n (可理解为事件A发生的频率)
互相独立的事件A与B 满足 P(A*B)=P(A)*P(B)
2)数学期望:随机变量X的数学期望EX是所有可能的值按照概率加权的和
期望的线性性质:E(X+Y)=E(X)+E(Y)
未完待续……