前言
要开始学数学(数论)了,于是特开一个专题来记录一下相关知识和有关问题。
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)\) 的动态规划也可用其解决。
例题
【P3758 可乐】
1.质数与约数
结论
-
对于一个足够大的整数,不超过 \(N\) 的质数大约有 \(\dfrac N{\ln N}\) 个,即每 \(\ln N\) 个数中有一个质数。
-
算数基本定理:正整数 \(N\) 可以唯一分解成有限个质数的乘积,即 \(N=p_1^{c_1}p_2^{c_2}\cdots p_m^{c_m}\) ,其中 \(p\) 都是质数, \(c\) 都是正整数,且满足 \(p_1 <p_2<\cdots<p_m\) .
-
\(N\) 的正约数个数为 \(\prod\limits_{i=1}^m (c_i+1)\) .
-
\(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\) 以外的质因数都不能筛它。
例题
质数距离
给定两个整数 \(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\) 来筛:
形象化地,如下图:
我们求和,相当于每次求一个竖列的和并相加。
用如上代码,我们可以便捷地: \(+\) 红(\(4\)),\(+\)绿(\(2\))\(\cdots\)
于是它成立。