数学学习笔记

前言

要开始学数学(数论)了,于是特开一个专题来记录一下相关知识和有关问题。

0.矩阵与快速幂

这里我认为与数学关系不是很大,于是一笔带过吧。

矩阵,是 \(n\times m\) 个数 \(a_{i,j}\) 组成的数表,称为 \(m\)\(n\) 列的矩阵。

记作: \(A=\begin{bmatrix}a_{1,1} & a_{2,1}&\cdots&a_{1,m}\\a_{2,1}&a_{2,2}&\cdots& a_{2,n}\\ \vdots&\vdots&\ddots&\vdots\\a_{n,1} & a_{n,2}&\cdots&a_{n,m} \end{bmatrix}\).

  • 特别定义:单位矩阵 \(I\) ,其中 \(a_{i,i} = 1\), 其余为 \(0\)
  • 其性质:任意次方等于自己,与任意矩阵相乘等于另一矩阵。

矩阵乘法

\(C=A\times B\)

  • \(A\)\(n\times r\) 的矩阵, \(B\)\(r\times m\) 的矩阵,那么 \(C\)\(n\times m\) 的矩阵。
  • 定义: \(C_{i,j} = \sum\limits_{k=1}^r A_{i,k}\times B_{k,j}\)

矩阵乘法满足交换律,不满足结合律。

另外,矩阵内可以内套矩阵。形如 \(P=\begin{bmatrix}A&B\\C&D\end{bmatrix}\) ,其中 \(A\)\(B\)\(C\)\(D\) 为矩阵。它仍然满足矩阵乘法的规律。

矩阵快速幂

顾名思义,矩阵快速幂,就是矩阵的多次乘方。过程用快速幂优化。

模板:【【模板】矩阵快速幂】。

应用

可以用它来解决形如 \(F_n=p\times F_{n-1}+q\times F_{n-2}+\cdots\) 的递推式。

甚至,形如 \(f(i,j) = \sum f(i,k)\times f(k,j)\) 的动态规划也可用其解决。

例题

P1349 广义斐波那契数列

P3758 可乐

1.质数与约数

结论

  1. 对于一个足够大的整数,不超过 \(N\) 的质数大约有 \(\dfrac N{\ln N}\) 个,即每 \(\ln N\) 个数中有一个质数。

  2. 算数基本定理:正整数 \(N\) 可以唯一分解成有限个质数的乘积,即 \(N=p_1^{c_1}p_2^{c_2}\cdots p_m^{c_m}\) ,其中 \(p\) 都是质数, \(c\) 都是正整数,且满足 \(p_1 <p_2<\cdots<p_m\) .

  3. \(N\) 的正约数个数为 \(\prod\limits_{i=1}^m (c_i+1)\) .

  4. \(N\) 的所有正约数和为

    \[\prod \limits_{i=1}^m\begin{pmatrix}\sum\limits_{j=0}^{c_i} (p_i)^j\end{pmatrix} \]

线性筛

先放代码

bool vis[M];
int pri[N],cnt;
void Prime(){
	for(int i = 2 ; i <= n ; i ++){
		if(!vis[i]) pri[++cnt] = i;
		for(int j = 1 ; j <= cnt && i * pri[j] <= n ; j ++){
			vis[i*pri[j]] = 1;
			if(!(i % pri[j])) break;
		}
	}
}

下面来详细解释:

vis[i] 表示 \(i\) 这个数是否被筛去过(其实也可以用它来记录某个数的最小质因子)。

其本质,是用每个数的最小质因子(即为 pri[j] )来筛去这个数。

为什么判断break在后面?

  • 考虑用 \(2\) 筛去 \(4\) .

正确性与复杂度的证明

以下内容参照 欧拉筛筛素数 - 学委 的博客

设一合数 \(C\)(要筛掉)的最小质因数是 \(p1\),令 \(B = \dfrac C {p_1}\)\(C = B \times p_1\)),则 \(B\) 的最小质因数不小于 \(p_1\) 否则 \(C\) 也有这个更小因子)。那么当外层枚举到 \(i = B\) 时,我们将会从小到大枚举各个质数;因为 \(i = B\) 的最小质因数不小于 \(p_1\),所以 \(i\) 在质数枚举至 \(p_1\) 之前一定不会 break这回\(C\) 一定会被 \(B \times p_i\) 删去。

注意这个算法一直使用“某数×质数”去筛合数,又已经证明一个合数一定会被它的最小质因数 \(p_1\) 筛掉,所以我们唯一要担心的就是同一个合数是否会被“另外某数 × \(p_1\) 以外的质数”再筛一次导致浪费时间。设要筛的合数是 \(C\),设这么一个作孽的质数为 \(p_x\),再令 \(A = \dfrac C {p_x}\)\(A\) 中一定有 \(p_1\) 这个因子。当外层枚举到 \(i = A\),它想要再筛一次 \(C\),却在枚举 \(Prime[j] = p_1\) 时,因为 \(i \mod Prime[j] == 0\) 就退出了。因而 \(C\) 除了 \(p_1\) 以外的质因数都不能筛它。

例题

质数距离

UVA10140 Prime Distance

给定两个整数 \(L,R\) ,求 \([L,R]\) 中相邻两个质数的差最大以及最小是多少,输出这两个质数。
\(1\leq L\leq R\leq2^{31}-1,0\leq R-L\leq 10^7\)

解析

\(L,R\) 很大,我们不能筛出所有的质数。

但是,可以筛出 \(\sqrt R\) 以内的质数,用这些质数来筛 \([L,R]\) 的质数,并存储在 \(p[i-l]\) 中。

int n,m;
int pri[N];bool vis[N];
void Prime(){
	for(int i = 2 ; i <= 1e6 ; i ++){
		if(!vis[i]) pri[++m] = i;
		for(int j = 1 ; j <= m && i * pri[j] <= 1e6 ; j ++){
			vis[i*pri[j]] = 1;
			if(!(i % pri[j])) break;
		}
	} 
}	
int p[N];
signed main(){
	ll l,r;
	Prime();
	while(scanf("%lld %lld",&l,&r) != EOF){
		memset(vis,0,sizeof(vis)); n = 0; 
		if(l == 1)vis[0] = 1;
		for(int j = 1 ; j <= m ; j ++)
			for(int i = l/pri[j] ; 1ll * i * pri[j] <= r ; i ++)
				if(i > 1)
					vis[i*pri[j]-l] = 1;
		for(int i = l ; i <= r ; i ++)
			if(!vis[i-l]) p[++n] = i;
		if(n < 2){puts("There are no adjacent primes.");continue;}
		int mx1 = p[1], mx2 = p[2],
			mn1 = p[1], mn2 = p[2];
		for(int i = 3 ; i <= n ; i ++){
			if(p[i]-p[i-1] > mx2-mx1) mx2 = p[i], mx1 = p[i-1];
			if(p[i]-p[i-1] < mn2-mn1) mn2 = p[i], mn1 = p[i-1];
		}
		printf("%d,%d are closest, %d,%d are most distant.\n",mn1,mn2,mx1,mx2);
	}
	return 0;
}

不定方程

不定方程

求不定方程: \(\dfrac 1x+\dfrac1y=\dfrac1{n!}\)的正整数解 \((x,y)\) 的数目。

\(n\leq10^6\).

解析

先把式子改写一下,变成 \(y=\dfrac{x\cdot n!}{x-n!}\)

\(t = x-n!\) , 则原式变为 \(y=n!+\dfrac {(n!)^2}t\) .

则问题转化为:求出 \((n!)^2\) 的约数总数。

根据上述的结论,可知对于正整数 \(N\) ,其约数共有 \(\prod(c_i+1)\)

那么,对于正整数 \(N^2\) ,其约数共有 \(\prod(2\times c_i+1)\)

对于求 \(n!\) 的约数,我们有如下做法:

for(int i = 1 ; i <= m ; i ++)
		for(ll j = pri[i] ; j <= n ; j *= pri[i])
			(c[i] += n/j) %= mod;

下面详细解释一下:

比如我们考虑 \(n=9\) , 先提取出其 \(2\) 的倍数为 \(9!=\cdots\times2\times4\times6\times8\times\cdots\).

使用质因子 \(2\) 来筛:

\[2=2\times\cdots\\4=2\times2\times\cdots\\6=2\times\cdots\\8=2\times2\times2\times\cdots\\\cdots \]

形象化地,如下图:

数学学习笔记_矩阵乘法
图源:conprour

我们求和,相当于每次求一个竖列的和并相加。

用如上代码,我们可以便捷地: \(+\) 红(\(4\)),\(+\)绿(\(2\)\(\cdots\)

于是它成立。