数论和数学
数论
欧几里得算法:快速计算两数最大公约数
对于两个正整数(m,n(m>n)),(gcd(m,n))表示它们的最大公约数,有(gcd(m,n)=gcd(n,m mod n))
证明:
[egin{align}
&设gcd(m,n)=p,m=pcdot m_1,n=pcdot n_1\
&设m=kcdot n+r\
&m mod n=r=pcdot m_1-kcdot n=pcdot m_1-pcdot kcdot n_1=p(m_1-kcdot n_1)\
&∵gcd(m_1,n_1)=1\
&∴gcd(m_1-kcdot 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)
证明:
[egin{align}
&设gcd(a,b)=p,a=pcdot a',b=pcdot 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)也有整数解
[egin{align}
&设a=kcdot b+r\
&设bx+ry=c的整数解为x_1,y_1\
&bx_1+ry_1=c\
&bx_1-kcdot 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;
}
乘法逆元
若有(xcdot yequiv1(mod p))则记(y=x^{-1})是(x)在模(p)意义下的乘法逆元
拓展欧几里得算法求逆元:
[egin{align}
&xcdot yequiv1(mod p)\
&xcdot y-kcdot 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个集合的方案数
递推式:
微积分
留个坑