为省赛整理的常用算法模板,数学部分
0-20的阶乘
1 | const long long fac[21]={1,1,2,6,24,120,720,5040,40320,362880, |
错排公式
有n个元素组成的排列,若所有的元素都不在自己原来的位置上,这样的排列记为D(n),有
D(n)=(n-1)[D(n-1)+D(n-2)]
理解博客
最小公倍数lcm(a,b)和最大公因数gcd(a,b)
1 | int gcd(int a,int b) |
扩展欧几里得原理
求解 ax + by = gcd(a; b) 这里得到的是一组 (x, y) 的可行解,(x + kb, y - ka) 为解集
如果是ax + by = d,这样的话得到的解就是 x0=x*(d/gcd(a,b) ) y0=y*(d/gcd(a,b) )
最后的解集就是:(x0 + k*(b/gcd(a,b) ), y0 - k*(a/gcd(a,b) ) )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15int e_gcd(int a,int b,int &x,int &y)
{
int d=a;
if(!b)
{
x=1;
y=0;
}
else
{
d=e_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
return d;
}
快速幂取模算法
1 | ll quick_mod(ll a,ll b,ll c) |
高精度问题
充分利用整数的取值范围,每4位进行处理,减小时间复杂度
1 | struct BigInt |
素数
埃式筛选法
1 | bool is_prime[MAXSIZE]; |
欧拉筛法
1 | int prime[maxn]; |
区间筛法
1 | bool is_prime[maxp]; |
大素数判断和分解(Miller-Rabin素数测试和Pollard Rho 大整数分解)
1 | //Miller_Rabin 算法进行素数测试 |
素性测试
1 | bool is_prime(int n) |
求一个数的因子个数
原理:
若一个整型数能够分解质因数!$ M=p_1^{X_1}*p_2^{X_2}*...*p_n^{X_n} $
则这个数的因子个数为 !$ W=(x_1+1)(x_2+1)...(x_n+1) $
根据这个原理我们就可以快速得到数的因子数表
模板:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void init_factor_table(int n)
{
fill(factor_table, factor_table + n + 1, 1);
for (int i = 2; i <= n; ++i)
{
if (factor_table[i] == 1) //从最小的素数开始计算
{
for (int j = i; j <= n; j += i)
{
int k = 0;
for (int m = j; m % i == 0; m /= i, ++k); //确定当前i的指数
factor_table[j] *= k + 1;
}
}
}
}
中国剩余定理
给出一个数被三个数取摸后的余数,求这个数。设这三个数为a,b,c;取摸之后的余数为a1,b1,c1。求解步骤:
- 对于每一个除数a,找出另两个数b,c的一个最小的满足m%a=1的公倍数m
- 用这个每个除数对应的余数乘以刚才得到的m,得到的三个数相加得到M
- 再用M除以a,b,c的最小公倍数,得到的结果就是想要的结果
1 | int CRT(int n,int a[],int mod[]) |
高斯消元
模板初级版本1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68void swaprow(double a[][MAX],int m,int maxrowe,int n) //交换m和主元行maxrowe
{
for(int k=m;k<=n+1;k++)
{
swap(a[m][k],a[maxrowe][k]);
}
}
void selectcole(double a[][MAX],int n) //选择列主元并进行消元
{
int i,j,k,maxrowe;
double temp;
for(j=1;j<=n;j++)
{
maxrowe=j;
for(i=j;i<=n;i++)
if(abs(a[i][j])>abs(a[maxrowe][j]))
maxrowe=i;
if(maxrowe!=j)
swaprow(a,j,maxrowe,n); //与最大主元所在的行进行交换
//消元,构成三角形矩阵
for(i=j+1;i<=n;i++)
{
temp=a[i][j]/a[j][j];
for(k=j;k<=n+1;k++)
a[i][k]-=a[j][k]*temp;
}
}
}
void gauss(double a[][MAX] ,int n) //最后的答案为a[][n+1]
{
selectcole(a,n); //构成上三角
for(int i=n;i>=1;i--)
{
for(int j=i+1;j<=n;j++)
a[i][n+1]-=a[i][j]*a[j][n+1];
a[i][n+1]/=a[i][i];
}
}
a数组为构成的增广矩阵,最后的解集为a[][n+1]
```
***
## 欧拉函数
小于n且与n互质的数的个数=n*(1-1/P1)*(1-1/P2)….*(1-1/Pn),其中Pn为不同的质因数
``` cpp
typedef long long ll;
map<ll,ll> prime_factor(ll t)
{
map<ll,ll> ret;
for(int i=2;i*i<=t;i++)
{
while(t%i==0) { ++ret[i];t/=i; }
}
if(t!=1) ret[t]=1;
return ret;
}
ll Euler (ll t)
{
ll ret=t;
map<ll,ll> fac=prime_factor(t);
for(map<ll,ll>::iterator i=fac.begin();i!=fac.end();i++)
ret=ret*(i->first-1)/i->first;
return ret;
}
Graham扫描法求凸包
从最左下方的开始,沿图的边界进行扫描,然后进行取舍1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
struct point
{
double x,y;
}p[maxn],t[maxn];
int n; //点的个数
//得到相应的叉乘
double get_X(point a,point b,point c) //ab到ac的叉乘计算
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double len(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(point &a,point &b)
{
double pp=get_X(p[0],a,b);
if(pp>0) return true;
if(pp<0) return false;
return len(p[0],a)<len(p[0],b);
}
int Graham() //返回凸包所含有的点的个数
{
if(n<=2) return 0;
else
{
for(int i=1;i<n;i++) //找到起始点
{
if(p[i].x<p[0].x) swap(p[i],p[0]);
else if(p[i].x==p[0].x && p[i].y<p[0].y) swap(p[i],p[0]);
}
sort(p+1,p+n,cmp);
t[0]=p[0];
t[1]=p[1];
int top=1; //栈顶位置
for(int i=2;i<n;i++)
{
while(t>0 && get_X(t[top-1],t[top],p[i])<=0) top--; //考虑的点在右侧的时候将栈顶的点弹出
top++;
t[top]=p[i];
}
return top;
}
}