数学总集/模板

数学专题模板总结★

\(\mathtt{Updated\ 2023/1/30.}\) 新增 \(Stirling\) 数和线性基。

题单贴贴


Part.1 质数

\(\mathcal{Eratosthenes}\)筛法

  • 核心思想:在找到一个质数 \(p\) 的时候,将 \(p\) 在范围内的倍数都标记一遍。以这种方式,下一个未被标记的数就是下一个质数。

  • 时间复杂度:\(O(N\ \log\ \log\ N)\) 已经很接近线性了

void get(int x){
	for(int i=2;i<=x;i++){
		if(!st[i]){
			p[++k]=i;
			for(int j=2;j*i<=x;j++)
				st[j*i]=true;
		}
	}
}

线性筛法

  • 核心思想:将每个合数以它最小的质因数来标记,确保每个合数只会被标记一遍。

  • 时间复杂度:\(O(N)\)

void get(int x){
	for(int i=2;i<=x;i++){
		if(!st[i])
			p[++k]=i;
		for(int j=1;j<=k && p[j]*i<=x;j++){
			st[p[j]*i]=true;
			if(!i%p[j]) break;
		}
	}
}

Part.2 约数

  • 前置芝士
    算术基本定理得,任意正整数 \(n\),可被表示为:\(n=p_1^{c_1}\times p_2^{c_2}\times ... \times p_k^{c_k}\),其中 \(c_i\) 都为正整数,\(p_i\) 都为质数,且有 \(p_1<p_2<...<p_k\)

\(n\) 的约数个数

\[num=(c_1+1)\times (c_2+1)\times ... \times (c_k+1) \]

  • 意思就是,对每个质数 \(p_i\),次数分别取 \(0\)\(c_i\) 不等时相互组合的所有情况。

\(n\) 的约数之和

\[\sum\limits_{i|n}^n i=(1+p_1+p_1^2+...+p_1^{c_1})\times (1+p_2+p_2^2+...+p_2^{c_2})\times ... \times (1+p_k+p_k^2+...+p_k^{c_k}) \]

  • \(n\) 的所有约数展开成算术基本定理形式,再提取公因数可导出这一公式。

\(n\) 的正约数集合——试除法

  • 扫描 \(2\sim\sqrt n\) 的所有数,即可求出 \(n\) 的所有正约数,时间复杂度为 \(O(\sqrt n)\);

  • 代码:

int f[N],k=0;
for(int i=1;i*i<=n;++i){
	if(n%i==0){
		f[++k]=i;
		if(i!=n/i) f[++k]=n/i;
	}
}

推论

  • 整数 \(n\) 的约数个数上界为 \(2\sqrt n\);

\(1\sim n\) 每个数的正约数集合——倍数法

  • 对于每个数 \(d\)\(1\sim n\) 中以 \(d\) 为约数的数就是 \(d\) 的倍数,时间复杂度 \(O(n\log n)\)

  • 代码:

vector<int>f[N];
for(int i=1;i<=n;++i){
	for(int j=1;j<=n/i;++j)
		f[i*j].push_back(i);

推论

  • \(1\sim n\) 中每个数约数的总和大约为 \(n\log n\);

最大公约数

欧几里得算法

  • 更详细的体验

  • \(\gcd(a,b)\) 表示 \(a\)\(b\) 的最大公约数,则易证:
    \(\gcd(b,a-b)=\gcd(a,a-b)=\gcd(b,a\mod b)=\gcd(a,b)\)
    \(\gcd(2a,2b)=2\times \gcd(a,b)\)

  • 函数写法:

int gcd(int a,int b){
  return (!b)?a:gcd(b,a%b);
}

最小公倍数

  • \(a\)\(b\) 的最小公倍数就是 \(\operatorname{lcm}(a,b)=a\times b/ \gcd(a,b)\);

定理

\[\forall a,b\in N,\ \gcd(a,b)\times \operatorname{lcm}(a,b)=a\times b \]

互质

  • 对于任意两个自然数 \(a,b\) ,如果有 \(\gcd(a,b)=1\),则这两个数称为互质;

欧拉函数

  • \(\varphi(n)\) 表示在 \(1\sim n\) 中与 \(n\) 互质的数的个数;

  • 易得,当 \(p\) 为质数时,\(\varphi(p)=p-1\)

  • 若将 \(n\) 表示为算术基本定理形式, \(n=p_1^{c_1}\times p_2^{c_2}\times ...\times p_k^{c_k}\),则有:

    \[\varphi(n)=n\times \frac{p_1-1}{p_1}\times\frac{p_2-1}{p_2}\times ... \times\frac{p_k-1}{p_k}=n\times \prod\limits_{质数p|n}(1-\frac{1}{p}) \]

性质

  • \(\forall n>1\)\(1\sim n\) 中与 \(n\) 互质的数的和为 \(n\times\varphi(n)/ 2\);

  • \(a,b\) 互质,则 \(\varphi(ab)=\varphi(a)\varphi(b)\),由此可见,欧拉函数为积性函数;

  • \(f\) 为积性函数,且在算术基本定理中有 \(n=\prod\limits_{i=1}^mp_i^{c_i}\),则 \(f(n)=\prod\limits_{i=1}^mf(p_i^{c_i})\)

  • \(p\) 为质数,若 \(p|n\)\(p^2|n\),则 \(\varphi(n)=\varphi(n/p)\times p\);

  • \(p\) 为质数,若 \(p|n\)\(p^2\nmid n\),则 \(\varphi(n)=\varphi(n/p)\times (p-1)\);

  • \(\sum\limits_{d|n}\varphi(d)=n\);

Part.3 同余

  • 定义:若 \(a \mod m=b\mod m\),则记为 \(a\equiv b\pmod{m}\)

欧拉定理

  • 若正整数 \(a,n\) 互质,则有 \(a^{\varphi(n)}\equiv 1\pmod{m}\);

推论

  • 若正整数 \(a,n\) 互质,则对于任意整数 \(b\)\(a^b\equiv a^{b\mod \varphi(n)}\pmod{n}\)

  • 特别地,当 \(a,n\) 不一定互质且 \(b>\varphi(n)\) 时,有 \(a^b\equiv a^{b\mod \varphi(n)+\varphi(n)}\pmod{n}\)

费马小定理

  • \(p\) 为质数,则对任意整数 \(a\)\(a^p\equiv a\pmod{p}\);

  • 转化:当 \(p\) 为质数时,对于任意整数 \(a\)有:
    \(a^{p-1}\equiv 1\pmod{p}\);

扩展欧几里得定理

  • 对于任意整数 \(a,b\),存在一对整数 \(x,y\),满足 \(ax+by=\gcd(a,b)\);

  • 代码:

int gcd(int a,int b,int &x,int &y){
	if(!b){
		x=1,y=0;
		return a;
	}
	int t=gcd(b,a%b,y,x);
	y-=1ll*(a/b)*x;
	return t;
}

乘法逆元

  • 之前的博客

  • 定义:假设有 \(a,b,p\), 满足 \(a\times b\equiv 1\pmod{p}\),则称 \(b\)\(a\)\(\pmod{p}\) 模式下的逆元。

  • 由之前费马小定理得,当在 \(\pmod{p}\) 意义下且 \(p\) 为质数时,就有 \(/a\) 等价于 \(\times a^{p-2}\)

推广:逆元的线性算法

  • 假设我们要求的是 \(i\)\(\pmod{M}\) 意义下的逆元。

    \(t=M/i\)\(k=M\%i\)\(inv[i]\)\(i\)\(\pmod{M}\)模式下的逆元,那么就有:

    \(t* i+k\equiv 0\pmod{M}\),即 \(M\equiv 0\pmod{M}\)

    \(-t* i\equiv k\pmod{M}\)\((-M+k)\equiv k\pmod{M}\)

    对于上面两个式子同时除以 \(i* k\),我们可以得到:

    \(-t* inv[k]\equiv inv[i]\pmod{M}\)

    再将 \(t\)\(k\) 置换回去,最终可以得到:

    \[inv[i]=(M-M/i)* inv[M\%i]\%M \]

    此时将 \(inv[1]\) 初始化为 \(1\),即可继续计算。

  • 代码:

for(int i=2;i<=n;i++)
	inv[i]=(long long)(p-p/i)*inv[p%i]%p;

*快速幂

  • 譬如求 \(a^b\),将 \(b\) 拆解成 \(2^k\) 相加的形式,再对 \(a\) 不断平方后计入结果即可。时间复杂度 \(O(\log _2 b)\)

  • 是求乘法逆元的好方法;

  • 代码:

int ksm(int a,int b){
	int cnt=1,dx=a;
	for(;b;b>>=1,dx*=dx)
		if(b&1)
			cnt*=dx;
	return cnt;
}

中国剩余定理

  • 对于方程组:
    \(\begin{cases}x\equiv a_1\pmod{m_1}\\x\equiv a_2\pmod{m_2}\\ \ \ \ \ \vdots\\x\equiv a_k\pmod{m_k}\end{cases}\)

    \(M=m_1\times m_2\times...\times m_k=\prod\limits_{i=1}^k m_i\),设 \(M_i=M/m_i\)\(t_i\)\(M_i\)\(\mod M\) 意义下的逆元,则有 \(x\equiv \sum\limits_{i=1}^k a_i t_i M_i\pmod{M}\)

  • 该定理的适用条件是 \(m_1,m_2,...,m_k\) 两两互质;

扩展

  • \(m_1,m_2,...,m_k\) 不一定两两互质时,考虑使用更朴素的 \(\operatorname{exgcd}\) 算法求解方程组;

  • \(M\)\(A\)\(M=\operatorname{lcm} (m_1,m_2,...,m_k)\)
    对于第 \(i\) 个方程式,对此时的 \(M_i=\operatorname{lcm} (m_1,m_2,...,m_{i-1})\)\(m_i\) 求解不定方程 \(xM_i+ym_i\equiv a_i-A\pmod{m_i}\)\(A=\sum\limits_{j=1}^{i-1} M_j\times x\)

  • 代码:

m=read(),a=read();
for(int i=2;i<=n;++i){
	mi=read(),ai=read();
	p=((ai-a)%mi+mi)%mi;
	d=exgcd(m,mi,x,y);
	x=x*abs(p/d)%mi;
	a+=m*x;
	m*=mi/d;
	a=(a%m+m)%m;
}

Part.4 矩阵乘法

矩阵相加

  • 有矩阵 \(a\)\(b\),它们的和矩阵 \(c\) 的第 \(i\) 行第 \(j\) 列的元素为 \(a_{i,j}+b_{i,j}\)

  • 相加的矩阵行数列数必须都相等;

矩阵相乘

  • 能相乘的矩阵需满足 \(a.size=x\times z\)\(b.size=z\times y\)

  • 转移方程为 \(c_{i,j}=\sum\limits_{k=1}^z a_{i,k}\times b_{k,j}\)

  • 函数写法:

void mul(int c[][N],int a[][N],int b[][N]){
	int cnt[N][N]={0};
	for(int i=1;i<=x;++i)
		for(int j=1;j<=y;++j)
			for(int k=1;k<=z;++k)
				cnt[i][j]+=a[i][k]*b[k][j];
	for(int i=1;i<=x;++i)
		for(int j=1;j<=y;++j)
			c[i][j]=cnt[i][j];
	return ;
}
  • 重载运算符写法:
struct memr{
	int a,b;
	ll x[N][N];
	memr operator*(const memr &_)const{
		memr cnt;
		memset(cnt.x,0,sizeof(cnt.x));
		cnt.a=a,cnt.b=_.b;
		for(int i=1;i<=a;++i)
			for(int j=1;j<=_.b;++j)
				for(int k=1;k<=b;++k)
					cnt.x[i][j]+=1ll*x[i][k]*_.x[k][j];
		return cnt;
	}
};

矩阵快速幂

  • 和快速幂原理一样,只是对象换成了矩阵(甚至如果你选择重载运算符的话就连长得都和普通快速幂一样);

特殊矩阵

斐波那契数列

  • 转移矩阵为 \(\begin{bmatrix}1&1\\1&0\end{bmatrix}\),设初始矩阵为 \(\begin{bmatrix}1\\0\end{bmatrix}\),则就表示 \(\begin{bmatrix}f(1)\\f(0)\end{bmatrix}\);

  • 用转移矩阵快速幂之后再乘以初始矩阵即得答案;

单位矩阵

  • 即与其相乘后仍得原矩阵的矩阵;

  • 一个 \(n\times n\) 的单位矩阵:\(\begin{bmatrix}1&0&\cdots&0&0\\0&1&\cdots&0&0\\\vdots&\ &\ddots&\ &\vdots\\0&0&\cdots&1&0\\0&0&\cdots&0&1\end{bmatrix}\)
    也就是只有当 \(i=j\) 时矩阵上的数字才为 \(1\),其余都是 \(0\)

Part.5 高斯消元

增广矩阵

  • \(M\)\(N\) 元线性方程组所有系数加上等号右边的常数,可以写成一个 \(M\times (N+1)\) 的矩阵;
    例如 :

    \[\begin{cases}x_1+2x_2-x_3=-6\\2x_1+x_2-3x_3=-9\\-x_1-x_2+2x_3=7\end{cases}\Rightarrow \begin{bmatrix}1&2&-1&|&-6\\2&1&-3&|&-9\\-1&-1&2&|&7\end{bmatrix} \]

求解步骤

  • 将当前要求的未知数系数绝对值最大的一行交换到上面;

  • 将不包括该行在内的下面所有行该未知数的系数都化为这个最大的系数;

  • 以下所有行都减去该行;

  • 换下一个未知数,然后重复操作;

  • 显然,当做完一轮这样的操作后,下面的所有行里当前未知数的系数都为 \(0\),也就是少了一个未知数;
    如果对整个矩阵都这样操作过后,最终得到的矩阵一定是一个倒三角(梯形)的形状;

  • 做完一轮以上操作,再从下往上扫,如果有解,则最后一行的方程一定是 \(ax_n=b(a\not =b)\),则 \(x_n\) 可以解出,再代回上一个方程即可解出 \(x_{n-1}\)。以此类推,就可以解出所有解;

解的情况

  • 无解

    • 在正推或倒推的时候,如果出现形如 \(0=b(b\not =0)\) 的式子,则方程一定无解;
  • 无数解

    • 在正推或倒推时,都没有出现无解的情况;
    • 正推和倒推过程中产生的不是 \(0=0\) 这样的式子不足 \(n\) 个;
  • 唯一解

    • 正推和倒推过程中没有无解现象;
    • 最后剩下的不是 \(0=0\) 的式子恰好 \(n\) 个;

Part.6 组合计数

加法原理

  • 其定义指,在完成某件事时,共有 \(n\) 类方法。每类方法都有 \(m_i\) 种实现,则最后完成该事件的总方法数应为 \(\sum\limits_{i=1}^n m_i\)

  • 该原理的使用特点为:每类方法都处于同一优先级,每类方法互相之间不干扰;

乘法原理

  • 其定义指,在完成某件事时,共有n个步骤。每个步骤都有 \(m_i\) 种实现,每种实现的效果一致,则最后完成该事件的总方法数应为\(\prod\limits_{i=1}^n m_i\)(即求 \(m_i\) 的积);

  • 该原理特点为:分步骤完成,每一步之间互相关联,缺一不可;

排列

  • \(n\) 个数中取 \(m\) 个,求其总共可能的排列数(即需要考虑不同数字之间的顺序,即使选择同一批数字,若顺序不同,也算是不同的答案)。

  • 公式为:\(A^m_n=n!÷m!\)

组合

  • \(n\) 个数中取 \(m\) 个,求其总共可能的组合数(即不需要考虑不同数字之间的顺序,只要选择的数字一样,都算作一种);

  • 公式为:\(C^m_n=n!÷m!÷(n-m)!\)
    也就是排列的公式再除以同一种数字组合下可能出现的排列情况;

性质

  • \(C^m_n=C^{n-m}_n\);

  • \(C^m_n=C^m_{n-1}+C^{m-1}_{n-1}\);

  • \(C^0_n+C^1_n+C^2_n+...+C^n_n=2^n\);

二项式定理

\(Lucas\) 定理

  • \(p\) 为质数时,对于任意整数 \(1\le m\le n\) 有:
    \(C^m_n\equiv C^{m\mod p}_{n\mod p}\times C^{m/p}_{n/p}\pmod{p}\)

\(Catalan\) 数列

  • 给定 \(n\)\(0\)\(n\)\(1\),它们按某种顺序排成长度为 \(2n\) 的序列,满足任意前缀中 \(0\) 的个数都不小于 \(1\) 的个数的序列的数量为:
    \(Cat_n=\frac{C^n_{2n}}{n+1}\)

  • 公式变形:
    \(Cat_n=\sum\limits_{i=0}^{n-1} Cat_{i}\times Cat_{n-1-i}\)
    \(Cat_n=C^n_{2n}-C^{n-1}_{2n}\)
    \(Cat_n=\frac{4n-2}{n+1}Cat_{n-1}\)

\(Catalan\) 数列相关的问题

  • \(n\) 个左括号和 \(n\) 个右括号组成的合法括号序列数量为 \(Cat_n\)

  • \(1\sim n\) 的整数经过一个栈,形成的合法出栈序列数量为 \(Cat_n\)

  • \(n\) 个节点构成的不同二叉树数量为 \(Cat_n\)

  • 平面直角坐标系上,每一步都只能向上或向右走,从 \((0,0)\) 走到 \((n,n)\) 且除了两端点外不接触直线 \(y=x\) 的路线数量为 \(2Cat_n-1\)

\(Stirling\) 数列

第一类 \(Stirling\)

  • 定义:用 \(n\) 个互不相同的数构成 \(k\) 个圆排列的方案数。

  • 递推公式:\(s_{n,k}=s_{n-1,k-1}+(n-1)\times s_{n-1,k}\),其中 \(s_{0,0}=1,s_{x,0}=0\)

第二类 \(Stirling\)

  • 定义:将 \(n\) 个不同的数分到 \(k\) 个不同组中的方案数。

  • 递推公式:\(S_{n,k}=S_{n-1,k-1}+k\times S_{n-1,k}\),其中 \(S_{0,0}=1,S_{x,0}=0\)

Part.7 容斥原理

容斥原理

  • \(S_1,S_2,S_3,...,S_n\) 为有限集合,\(|S|\) 表示集合 \(S\) 的大小,则:
    \(|\bigcup\limits_{i=1}^n S_i|=\sum\limits_{i=1}^n|S_i|-\sum\limits_{1\le i<j\le n}|S_i\cap S_j|+\sum\limits_{1\le i<j<k\le n}|S_i\cap S_j\cap S_k|+...+(-1)^{n+1}|S_1\cap...\cap S_n|\)

\(Mobius\) 函数

  • 设正整数 \(N\) 按照算术基本定理分解质因数为 \(N=p_1^{c_1}\times p_2^{c_2}\times ...\times p_m^{c_m}\),定义函数

\[\mu(N)=\begin{cases}0&\exists\ i\in [1,m],c_i>1\\1&m\equiv 0\pmod{2},\forall\ i\in[i,m],c_i=1\\-1&m\equiv 1\pmod{2},\forall\ i\in[1,m],c_i=1\end{cases} \]

\(\mu(N)\)\(Mobius\) 函数(莫比乌斯函数)。

  • 可以发现,\(Mobius\) 函数其实就是普通容斥原理的每一项的系数;

Part.? 线性基

  • 之前集训做过,但现在才算是理解;

定义

  • 一组数,其中若干异或之后可以用于描述其对应的一个序列中的任意数。

性质:

  • 异或后可得原序列任意数,但不可能得到 \(0\)

  • 不存在重复数字和无用数字,也就是说数字个数最少;

构造方式

  • 一种显然的构造方法是按照 \(2^0,2^1,2^2...\) 的方式来构造,但对于给定序列,这种构造方式不一定最简;

  • 由上可以看出一个性质,构造出来的线性基的元素个数一定不超过 \(\log x_{max}\)(原序列中最大数),因为按照上面那种方式构造也只需要这么多个元素即可。
    因此想到线性基元素一定是根据为 \(1\) 的最高位来构造的。

  • 构造策略:
    首先我们有一个数列。先扫进来一个数。
    我们的线性基是按最高位记录的,所以我们从高到低扫描这个数的二进制位是否为 \(1\),如果不是就下一位,如果是的话:

    • 当前线性基这一位已有值 \(p_i\),则用 \(p_i\) 异或消掉当前数的这一位,然后继续扫描下一位;
    • 当前线性基这一位没有值,则 \(p_i\) 直接赋值为当前这个数,然后不再扫描下一位。
参考代码
void check(ll x){
	for(int i=52;i>-1;--i){
		if(!((x>>i)&1)) continue;
		if(!p[i]){
			p[i]=x;
			break;
		}
		x^=p[i];
	}
	return ;
}

相关应用

  • 求异或最大值
code
ll cnt=0;
for(int i=52;i>-1;--i)
	if((cnt^p[i])>cnt)
		cnt^=p[i];
  • 求异或最小值
code
ll cnt=0;
for(int i=0;i<52;++i)
	if(p[i]){
 		cnt=p[i];
		break;
	}
  • 求给定数能否从线性基中异或得出
code
bool ask(ll x){
	for(int i=52;i>-1;--i)
 		if((x>>i)&1)
			x^=p[i];
	return !x;
}
posted @ 2022-06-12 11:31  Star_LIcsAy  阅读(110)  评论(0)    收藏  举报