数论和数学
数论
欧几里得算法:快速计算两数最大公约数
对于两个正整数\(m,n(m>n)\),\(gcd(m,n)\)表示它们的最大公约数,有\(gcd(m,n)=gcd(n,m\ mod\ n)\)
证明:
\[\begin{align} &设gcd(m,n)=p,m=p\cdot m_1,n=p\cdot n_1\\ &设m=k\cdot n+r\\ &m\ mod \ n=r=p\cdot m_1-k\cdot n=p\cdot m_1-p\cdot k\cdot n_1=p(m_1-k\cdot n_1)\\ &∵gcd(m_1,n_1)=1\\ &∴gcd(m_1-k\cdot n_1,n_1)=1\\ &∴gcd(n,m\ mod\ n)=p \end{align} \]
根据这个性质,就可将\(m,n\)不断转换直到其中一个为0,另一个就是最大公约数
代码:
int gcd(int m,int n)
{
if( n==0 ) return m;
else return gcd(m,m%n);
}
拓展欧几里得算法:对方程\(ax+by=c\)进行快速求解
裴蜀定理:方程\(ax+by=c\)有整数解当且仅当\(gcd(a,b)|c\)
证明:
\[\begin{align} &设gcd(a,b)=p,a=p\cdot a',b=p\cdot b'\\ &p(a'x+b'y)=c\\ &∵a',x,b',y都是整数\\ &∴p|c即gcd(a,b)|c \end{align} \]
如果方程\(ax+by=c\)有整数解,那么根据裴蜀定理和欧几里得算法可以得出\(bx+(a\ mod\ b)y=c\)也有整数解
\[\begin{align} &设a=k\cdot b+r\\ &设bx+ry=c的整数解为x_1,y_1\\ &bx_1+ry_1=c\\ &bx_1-k\cdot by_1+ay_1=c\\ &b(x_1-ky_1)+ay_1=c\\ &即方程ax+by=c的整数解为y_1,x_1-ky_1 \end{align} \]
根据这个可以不停进行转化直到方程变为\(ax=c\)
代码:
void exgcd(int a,int b)
{
if( b==0 )
{
x=c/a;
y=0;
return;
}
exgcd(b,a%b);
int z=x;
x=y;
y=z-y*(a/b);
return;
}
乘法逆元
若有\(x\cdot y\equiv1(mod\ p)\)则记\(y=x^{-1}\)是\(x\)在模\(p\)意义下的乘法逆元
拓展欧几里得算法求逆元:
\[\begin{align} &x\cdot y\equiv1(mod\ p)\\ &x\cdot y-k\cdot p=1\\ &即y和-k是关于a,b的方程ax+bp=1的两个整数解,运用拓展欧几里得算法即可求得 \end{align} \]
Eratosthenes 筛法
从2扫到n,如果没有被标记,那么这个数就是素数,将它在n以内的倍数都打上标记
代码:
int prime[N];
bool flag[N];
void Prime(int n)
{
for(int i=2;i<=n;i++)
if( !flag[i] )
{
prime[++prime[0]]=i;
for(int j=2;j*i<=n;j++) flag[i*j]=1;
}
return;
}
欧拉筛法
在线性时间复杂度内求出n以内的素数
在Erathosthenes筛法的基础上进行改进,我们保证每一个合数只被它最小的质因子筛掉,我们记录每一个数的最小质因子,然后不断更新即可
int prime[N],mprime[N];
void Prime(int n)
{
for(int i=2;i<=n;i++) mprime[i]=i;
for(int i=2;i<=n;i++)
{
if( mprime[i]==i ) prime[++prime[0]]=i;
for(int j=1;j<=prime[0] && prime[j] <= mprime[i];i++)
mprime[prime[j]*i]=prime[j];
}
return;
}
数学
快速幂
把指数二进制分解,快速算出\(a^x\)
代码:
int fpow(int a,int b)
{
int ans=1,base=a;
while( b )
{
if( b%2 ) ans*=base;
base*=base;
b/=2;
}
return ans;
}
数学期望
每一种情况的概率和权值相加
线性代数
矩阵
从上到下进行消元得到一个上三角矩阵,然后将对角线乘积相加就得到行列式的值
高斯消元
从上到下进行消元然后在从下到上带回去
代码:
int Gauss(int equ,int var)
{
for(int i=0; i<=var; i++)
{
x[i]=0;
freeX[i]=true;
}
int col=0;
int row;
for(row=0; row<equ&&col<var; row++,col++)
{
int maxRow=row;
for(int i=row+1; i<equ; i++)
{
if(abs(a[i][col])>abs(a[maxRow][col]))
maxRow=i;
}
if(maxRow!=row)
{
for(int j=row; j<var+1; j++)
swap(a[row][j],a[maxRow][j]);
}
if(a[row][col]==0)
{
row--;
continue;
}
for(int i=row+1; i<equ; i++)
{
if(a[i][col]!=0)
{
int lcm=LCM(abs(a[i][col]),abs(a[row][col]));
int ta=lcm/abs(a[i][col]);
int tb=lcm/abs(a[row][col]);
if(a[i][col]*a[row][col]<0)
tb=-tb;
for(int j=col; j<var+1; j++)
{
a[i][j]=a[i][j]*ta-a[row][j]*tb;
}
}
}
}
for(int i=row; i<equ; i++)
if (a[i][col]!=0)
return -1;
int temp=var-row;
if(row<var)
return temp;
for(int i=var-1; i>=0; i--)
{
int temp=a[i][var];
for(int j=i+1; j<var; j++)
{
if(a[i][j]!=0)
temp-=a[i][j]*x[j];
}
if(temp%a[i][i]!=0)
return -2;
x[i]=temp/a[i][i];
}
return 0;
}
组合数学
二项式定理
相当于在n个(x+y)中选k个为x,n-k个为y
斯特林数
第一类Stirling数表示将 n 个不同元素构成m个圆排列的数目
递推式:
第二类Stirling数实际上是集合的一个拆分,表示将n个不同的元素拆分成m个集合的方案数
递推式:
微积分
留个坑