整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


目录

  • 0x00. 矩阵
    • 0x01. 矩阵
    • 0x02. 矩阵的加法与数量乘法
    • 0x03. 矩阵乘法
    • 0x04. 矩阵加速递推
    • 0x5. 矩阵的简单应用
      • 0x05.1 zoj 2853 Evolution
    • 0x06. 矩阵表达修改
    • 0x07. 矩阵在图的邻接矩阵中的应用
      • 0x07.1 定长路径统计
      • 0x07.2 定长最短路
      • 0x07.3 限长路径计数/最短路
    • 0x08. Porblem A
    • 0x09. Problem B
    • 0xA. Problem C
    • 0xB. Problem D
    • 0xC. Problem E
    • 0xD. Problem F
    • 0xE. Problem G
  • 0x10. 行列式
    • 0x11. 行列式
    • 0x12. 上三角行列式
    • 0x13. Problem H
    • 0x14. 积和式
    • 0x15. 余子式
    • 0x16. 行列式按一行展开
    • 0x17. 矩阵的逆
    • 0x18. 矩阵的相似
    • 0x19. 矩阵的特征值与特征向量
    • 0x1A. Problem I
  • 0x20. 高斯消元法
    • 0x21. 高斯消元解线性方程组
    • 0x22 高斯-约旦消元法
    • 0x23. 高斯消元解异或线性方程组
    • 0x24. 高斯消元解模线性方程组
    • 0x25. 矩阵求逆
    • 0x26. 行列式计算
    • 0x27. 生成树计数
  • 0x30. 线性空间
    • 0x31. 线性空间
    • 0x32. 线性相关
    • 0x33. 极大线性无关组
    • 0x34. 向量组的秩
    • 0x35. 线性基
      • 0x35.1 前置知识
      • 0x35.2 求线性基
      • 0x35.3 线段树套线性基
      • 0x35.6 线性基全家桶(code)
      • 0x35.7 线性基总结
  • 0x40. 最后的话

0x00. 矩阵

0x01. 矩阵

  • 由n×mn×mn×m 个数排成 nnn 行,mmm 列的一张表称为一个 n×mn×mn×m 矩阵
  • 矩阵中的每一个数称为这个矩阵的一个元素。
  • 矩阵的第i行与第j列交叉位置的元素称为矩阵的(i,j)(i,j)(i,j)元。
  • 若一个矩阵的行与列相等,则称它为方阵

{3x0+2x1−5x2=−12x0−4x1+x2=1−x0−5x1+x2=−6→[32−5−12−411−1−51−6]\begin{cases}3x_0+2x_1-5x_2=-1 & \\2x_0-4x_1+x_2=1 & \\ -x_0-5x_1+x_2=-6 & \\ \end{cases} \rightarrow\begin{bmatrix} 3 & 2 & -5 & -1 \\ 2 & -4 & 1 & 1 \\ -1 & -5 & 1 & -6 \\\end{bmatrix}⎩⎪⎨⎪⎧​3x0​+2x1​−5x2​=−12x0​−4x1​+x2​=1−x0​−5x1​+x2​=−6​​→⎣⎡​32−1​2−4−5​−511​−11−6​⎦⎤​

0x02. 矩阵的加法与数量乘法

设 AAA 和 BBB 均为 n×mn×mn×m 矩阵,且∀i∀jCi,j=Ai,j+Bi,j\forall i \forall j C_{i,j}= A_{i,j}+B_{i,j}∀i∀jCi,j​=Ai,j​+Bi,j​
则称 CCC 为 AAA 与 BBB 的和,记作 C=A+BC=A+BC=A+B

设 AAA 为 n×mn×mn×m 矩阵,且∀i∀jCi,j=k×Ai,j\forall i \forall j C_{i,j}= k \times A_{i,j}∀i∀jCi,j​=k×Ai,j​
则称 CCC 为 kkk 与 AAA 的数量乘积,记作C=kAC=kAC=kA

0x03. 矩阵乘法

设AAA为s×ns×ns×n矩阵,BBB为n×mn×mn×m矩阵,令CCC为s×ms×ms×m矩阵,且

Ci,j=∑k=1nai,k×bk,jC_{i,j}=\sum_{k=1}^{n}a_{i,k} \times b_{k,j}Ci,j​=k=1∑n​ai,k​×bk,j​

则称CCC为AAA与BBB的乘积,记作C=ABC=ABC=AB

  • 只有左矩阵的列数与右矩阵的行数相同时才能相乘
  • 乘积矩阵的 (i,j)(i,j)(i,j) 元等于左矩阵的第 iii行与右矩阵的第 jjj 列对应元素乘积之和
  • 乘积矩阵的行数等于左矩阵的行数,列数等于右矩阵的列数

为什么我们要这样定义矩阵乘法?
在二维平面上考虑一个点A(x,y)A(x,y)A(x,y)
将线段OAOAOA逆时针旋转θθθ,则AAA点所对应的A′A'A′点的坐标是什么?

令 r=x2+y2r = \sqrt{x^2+y^2}r=x2+y2​,ααα为∠AOx∠AOx∠AOx,则有

x=rcosα,y=rsinαx=rcosα,y=rsinαx=rcosα,y=rsinα
x′=rcos(α+θ),y′=rsin(α+θ)x'=rcos(α+θ) , y'=rsin(α+θ)x′=rcos(α+θ),y′=rsin(α+θ)
x′=rcosαcosθ−rsinαsinθ=xcosθ−ysinθx'=rcosαcosθ-rsinαsinθ=xcosθ-ysinθx′=rcosαcosθ−rsinαsinθ=xcosθ−ysinθ
y′=rsinαcosθ+rcosαsinθ=xsinθ+ycosθy'=rsinαcosθ+rcosαsinθ=xsinθ+ycosθy′=rsinαcosθ+rcosαsinθ=xsinθ+ycosθ

x′=xcosθ−ysinθx'=xcosθ-ysinθx′=xcosθ−ysinθ
y′=xsinθ+ycosθy'=xsinθ+ycosθy′=xsinθ+ycosθ
将方程的系数拿出来排成一张表:

则该矩阵表示了转角为 θθθ 的旋转

则该矩阵表示了转角为 φφφ 的旋转



一般使用一个二维数组来模拟矩阵。

需要注意的是,因为数组下标是从0开始的,所以我们推出来的矩阵里的下标在实际使用中都要−1-1−1。详见下题。

0x04. 矩阵加速递推

AcWing 1303. 斐波那契前 n 项和



上述结论证明链接

const int N = 3;
int n, m;
int a[N][N];
int f[N];
void mul(int c[], int a[], int b[][N])
{int tmp[N] = {0};for(int j = 0; j < N; ++ j)//2的行for(int k = 0; k < N; ++ k)//1的列2的行,1的列=2的行//只有一行,求这一行的每一列tmp[j] = (tmp[j] + (ll)a[k] * b[k][j]) % m;memcpy(c, tmp, sizeof tmp);
}
void mul(int c[][N], int a[][N], int b[][N])
{int tmp[N][N] = {0};for(int i = 0; i < N; ++ i)for(int j = 0; j < N; ++ j)for(int k = 0; k < N; ++ k)tmp[i][j] = (tmp[i][j] + (ll)a[i][k] * b[k][j]) % m;memcpy(c, tmp, sizeof tmp);
}int main()
{scanf("%d%d", &n, &m);    int f1[N] = {1, 1, 1};int a[N][N] = {0, 1, 0,1, 1, 1,0, 0, 1};n -- ;//这里是从第一项开始的,所以要--;//就是说数组模拟,是从f[0]开始的,f[0]就是斐波那契数列里的f_1 //所以我们输出的是f2而不是式子中的f3
/*  若求的是斐波那契第n项的值,只需要把f和a改成下面的矩阵,n不需要-1,最后输出f[0]即可。int f[N] = {0, 1};int a[N][N] = {{0, 1}, {1, 1}};printf("%d\n", f[0]);
*/while(n){if(n & 1) mul(f1, f1, a);mul(a, a, a);n >>= 1;}    printf("%d\n", f1[2]);return 0;
}

AcWing 1304. 佳佳的斐波那契



计算公式为:Tn=n∗Sn−PnT_n = n * S_n - P_nTn​=n∗Sn​−Pn​

const int N = 4;
int n, m;
int f[N][N];
void mul(int c[][N], int a[][N], int b[][N])
{static int tmp[N][N];memset(tmp, 0, sizeof tmp);for(int i = 0; i < N; ++ i)for(int j = 0; j < N; ++ j)for(int k = 0; k < N; ++ k)tmp[i][j] = (tmp[i][j] + (ll)a[i][k] * b[k][j]) % m;memcpy(c, tmp, sizeof tmp);
}int main()
{scanf("%d%d", &n, &m);// {fn, fn+1, sn, pn}// pn = n * sn - tnint f1[N][N] = {1, 1, 1, 0};int a[N][N] = {{0, 1, 0, 0},{1, 1, 1, 0},{0, 0, 1, 1},{0, 0, 0, 1},};int k = n - 1;while(k){if(k & 1)mul(f1, f1, a);mul(a, a, a);k >>= 1;}cout << (((ll)n * f1[0][2] - f1[0][3] % m + m) % m) << endl;return 0;
}

0x5. 矩阵的简单应用

  • 将行列式的变换动作构造成一个矩阵(如 平移 旋转 翻转 等操作)
  • 将一个一次递推式 构造出 矩阵。
      例如:fibonaccifibonaccifibonacci 数列的递推关系 f(n)=f(n−1)+f(n−2)f(n) = f(n-1) + f(n-2)f(n)=f(n−1)+f(n−2)
    我们可以将矩阵构造为:
      (0111)(ab)=(ba+b)\left( \begin{matrix} 0& 1\\ 1& 1\end{matrix} \right) \left( \begin{matrix} a\\ b\end{matrix} \right) =\left( \begin{matrix} b\\ a+b\end{matrix} \right)(01​11​)(ab​)=(ba+b​)

0x05.1 zoj 2853 Evolution

题目:

它就是先进行构造“进化”这个动作的矩阵。

有多少次进化=初始物种状态矩阵×(进化矩阵)n有多少次进化=初始物种状态矩阵 \times (进化矩阵)^n有多少次进化=初始物种状态矩阵×(进化矩阵)n

#include<cstdio>
#include<cstring>
int n;
struct matrix
{double a[200][200];
} ans, base;matrix multiply(matrix x, matrix y)
{matrix tmp;for (int i = 0; i < n; i++)for (int j = 0; j < n; j++){tmp.a[i][j] = 0;for (int k = 0; k < n; k++) tmp.a[i][j] += x.a[i][k] * y.a[k][j];}return tmp;
}
void fast_mod(int k)
{while (k){if (k & 1) ans = multiply(ans, base);base = multiply(base, base);k >>= 1;}
}
int main()
{int m, t, xi, yi;double s, zi, fin;int num[205];while (~scanf("%d%d", &n, &m) && !(n == 0 && m == 0)){for (int i = 0; i < n; i++) scanf("%d", &num[i]);scanf("%d", &t);memset(base.a, 0, sizeof(base.a));for (int i = 0; i < n; i++) base.a[i][i] = 1;   //初始为进化成自己while (t--){scanf("%d%d%lf", &xi, &yi, &zi);base.a[xi][yi] += zi;base.a[xi][xi] -= zi;}memset(ans.a, 0, sizeof(ans.a));for (int i = 0; i < n; i++) ans.a[i][i] = 1;fast_mod(m);fin = 0;for (int i = 0; i < n; i++)fin += num[i] * ans.a[i][n-1];printf("%.0f\n", fin);}return 0;
}

0x06. 矩阵表达修改

0x07. 矩阵在图的邻接矩阵中的应用

0x07.1 定长路径统计

0x07.2 定长最短路

0x07.3 限长路径计数/最短路

0x08. Porblem A

幽鬼是游戏Dotb的一个英雄,有个被动技能“折射”,可以将受到的伤害的一定百分比平均反弹给周围的其他英雄

现在有N个幽鬼,每个幽鬼有不同折射率。给出每个幽鬼可以折射到的英雄与幽鬼的折射率,并在一开始对第K个幽鬼造成P点伤害,求最后每个幽鬼受到的伤害。

例:若1号幽鬼能反弹到2号幽鬼,折射率为60%,2号幽鬼不能反弹到1号幽鬼,且在最初给予1号幽鬼400点伤害,则最后1号幽鬼受到160点伤害,2号幽鬼受到240点伤害
折射进行无数次
N<=100N<=100N<=100

Solution

设第i个幽鬼受到的总伤害为XiX_iXi​
则对于每个幽鬼可以列出一个关于伤害的方程,共N个方程
解方程组即可。
O(N3)O(N^3)O(N3)

0x09. Problem B

给定A[0],A[1]A[0],A[1]A[0],A[1],A[i]=3×A[i−1]−2×A[i−2](i>1)A[i]=3×A[i-1]-2×A[i-2] (i>1)A[i]=3×A[i−1]−2×A[i−2](i>1),求A[N]A[N]A[N]。
答案模100000000710000000071000000007输出,N<=109N<=10^9N<=109

Solution

形如F[i]=A[1]∗F[i−1]+A[2]∗F[i−2]+...+A[c]∗F[i−c]F[i]=A[1]*F[i-1]+A[2]*F[i-2]+...+A[c]*F[i-c]F[i]=A[1]∗F[i−1]+A[2]∗F[i−2]+...+A[c]∗F[i−c]的方程均可以利用矩阵乘法和快速幂进行优化

直接求解的时间复杂度为O(cN)O(cN)O(cN)

使用矩阵乘法的时间复杂度为O(c3logN)O(c^3logN)O(c3logN)

适用c较小而N较大的情况

代码同上面的矩阵快速幂

0xA. Problem C

在一个古老的国家对数字有这样的规定:这个国家适用0,1,2,3,4,5,6,7,8,90,1,2,3,4,5,6,7,8,90,1,2,3,4,5,6,7,8,9这101010个数字,但规定有PPP个整数对(xi,yi)(0<=xi,yi<=9)(xi,yi)(0<=xi,yi<=9)(xi,yi)(0<=xi,yi<=9)不能连续出现。
一个整数对(xi,yi)(xi,yi)(xi,yi)表示在一个数中,xi的后面不能紧跟着yi
现在我们要知道在这个规定下,第K小的正整数的前50位是多少。
K<=2∗109K<=2*10^9K<=2∗109

Solution

0xB. Problem D

给一张 NNN 个点的有向图,每条边的权值是111 ~ 555之间的整数,求从 SSS 走到 TTT 长度为 KKK 的路径条数,答案模100000310000031000003输出,N<=10,K<=109N<=10, K<=10^9N<=10,K<=109

Solution

有如下重要结论:
考虑一张无权的邻接矩阵 AAA
设B=A2B=A^2B=A2,则BijB_{ij}Bij​ 表示从 iii 点到jjj 点,走过两条边的路径条数

稍加扩展即可得到:

设C=AkC=A^kC=Ak,则CijC_{ij}Cij​表示从 iii 点到 jjj 点,走过 kkk 条边的路径条数

这是路径长度为1的情况

由于路径长度为111 ~ 555之间的整数,可以拆点后再使用上述结论

时间复杂度O((5N)3logK)O((5N)^3logK)O((5N)3logK)

0xC. Problem E

bzoj2676 Contra

给定N,R,Q,SN,R,Q,SN,R,Q,S
有N个关卡,初始有 QQQ 条命,且任意时刻最多只能有 QQQ 条命

每通过一个关卡,会得到 uuu 分和 111 条命,其中u=min{u=min \{u=min{ 最近一次连续通过的关数 ,R},R\},R}
若没有通过这个关卡,将失去一条命,并进入下一个关卡

若没有生命或N个关卡均已挑战过一次时,游戏结束,得到的分数为每关得到的分数的总和
每条命通过每个关卡的概率为p(0<=p<=1)p(0<=p<=1)p(0<=p<=1),原先最高分纪录为S
求当p至少为多少时,期望获得的总分能够超过最高分。

1<=N<=108,1<=R<=20,1<=Q<=51<=N<=10^8 ,1<=R<=20 ,1<=Q<=51<=N<=108,1<=R<=20,1<=Q<=5,输出保留6位小数

Solution

例如,当N=12,R=3,Q=2时
第一关:未通过 u=0 获得分数0 总分为0 剩余生命1
第二关:通过 获得分数1 总分为1 剩余生命2
第三关:通过 获得分数2 总分为3 剩余生命2
第四关:通过 获得分数3 总分为6 剩余生命2
第五关:通过 获得分数3 总分为9 剩余生命2
第六关:未通过 获得分数0 总分为9 剩余生命1
第七关:通过 获得分数1 总分为10 剩余生命2
第八关:未通过 获得分数0 总分为10 剩余生命1
第九关:未通过 获得分数0 总分为10 剩余生命0
游戏结束 总分为10

容易想到二分答案p
令g[i,j,k]g[i,j,k]g[i,j,k]表示在第 iii 关,uuu 值为 jjj ,还剩 kkk 条生命的概率

g[i+1,min(j+1,R),min(k+1,Q)]+=g[i,j,k]×pg[i+1,min(j+1,R),min(k+1,Q)]+=g[i,j,k]×pg[i+1,min(j+1,R),min(k+1,Q)]+=g[i,j,k]×p

g[i+1,0,k−1]+=g[i,j,k]×(1−p)g[i+1,0,k-1]+=g[i,j,k]×(1-p)g[i+1,0,k−1]+=g[i,j,k]×(1−p)

Answer+=g[i,j,k]×p×(j+1)Answer+=g[i,j,k]×p×(j+1)Answer+=g[i,j,k]×p×(j+1)

将后两维展开为一维数组进行矩阵乘法
复杂度为O(T(RQ)3logN)O(T(RQ)^3logN)O(T(RQ)3logN),其中TTT为二分的次数。

这个复杂度仍然过大,需要继续优化

我们发现,当u>=Q−1u>=Q-1u>=Q−1时,生命个数一定是QQQ

大量状态无用

去掉无用的状态后,矩阵的边长由PQPQPQ级别降为P+QP+QP+Q级别

再进行矩阵乘法,时间复杂度为O(T(R+Q)3logN)O(T(R+Q)^3logN)O(T(R+Q)3logN),其中TTT为二分次数。

这样可以通过所有的测试数据

☼即使使用时间复杂度较大的算法,加入常数优化也可以通过所有测试数据。

0xD. Problem F

给定NNN个点的无权有向图,询问长度<K<K<K的环有几个,答案模MMM输出,N<=35K<=1000000N<=35 K<=1000000N<=35K<=1000000

Solution

令f[k,i,j]f[k,i,j]f[k,i,j]表示长度为kkk,从iii走到jjj的方案数

则答案为
∑i=0n−1∑j=2k−1F[j,i,i]\sum_{i=0}^{n-1} \sum_{j=2}^{k-1}F[j,i,i]i=0∑n−1​j=2∑k−1​F[j,i,i]

暴力计算?O(KN3)O(KN^3)O(KN3)

将矩阵直接展开进行优化?O(N6logk)O(N^6logk)O(N6logk)

将K按二进制位分为若干个子问题
容易算出 2i2^i2i 的方案数的和
容易算出一个长度 kkk 时的方案数
将其相乘,再将子问题的答案求和即可
时间复杂度O(N3logk)O(N^3logk)O(N3logk)

0xE. Problem G

给定A[0],A[1]A[0],A[1]A[0],A[1],A[i]=3×A[i−1]−2×A[i−2](i>1)A[i]=3×A[i-1]-2×A[i-2] (i>1)A[i]=3×A[i−1]−2×A[i−2](i>1),求A[N]A[N]A[N]。
,答案模1010010^{100}10100输出,N<=109N<=10^9N<=109

Solution

构造这样的矩阵,可以利用矩阵的特征值来完成

需要用到线性代数的一些简单知识

0x10. 行列式

0x11. 行列式


0x12. 上三角行列式


直接使用定义 O(n!)O(n!)O(n!)
状态压缩DP O(2n)O(2^n)O(2n)

我们需要一个更优秀的算法来解决这个问题!





利用性质三和性质四
我们可以用类似高斯消元的方法
将一个普通的行列式通过初等行变换转化为一个上三角行列式进行求值

O(N3)O(N^3)O(N3)

代码见0x26节

0x13. Problem H

给定一张N个点M条边的有向无环图
保证有恰好K个点入度为0,我们称之为源;K个点出度为0,我们称之为汇,将源与汇的K个点分别标号为1..K1..K1..K
从图中选出K条没有公共点的路径,每条路径经过一个源与一个汇。不妨设从源i出发的边到达了汇TiT_iTi​。
对于任意i,ji,ji,j,若有i<j且Ti>TjT_i>T_jTi​>Tj​,我们称之为一个逆序对。
如果所有路径中的逆序对个数是偶数,答案增加1,否则答案减少1
求答案模一个素数p后的值
N<=600,M<=105,2<=p<=1000000007N<=600,M<=10^5,2<=p<=1000000007N<=600,M<=105,2<=p<=1000000007

Solution

注意到相交的路径不影响答案
e.g.
1->2->3
4->2->5

1->2->5
4->2->3
成对出现,且一个答案为1,一个答案为-1
因此不需要考虑路径相交的情况

令A[i,j]表示从第i个源到第j个汇的路径数
简单dp即可得到A
易发现答案即为A的行列式值
O(N3+NM)O(N^3+NM)O(N3+NM)

0x14. 积和式

0x15. 余子式

0x16. 行列式按一行展开


Solution

来自论文:斐波那契数列的矩阵和行列式表示

0x17. 矩阵的逆

若A×B=EA \times B=EA×B=E,则我们称B是A的逆,记作B=A−1B=A^{-1}B=A−1

行列式值不为0的n阶矩阵必有逆矩阵
如何求逆矩阵?

将A通过初等行变换变为单位矩阵,再将单位矩阵按同样顺序做相同的初等行变换,则得到的矩阵即为A−1A^{-1}A−1。

我们可以利用高斯消元解决矩阵求逆的问题。

struct M {ll f[NN][NN * 2];int Gauss() {for (int i = 1; i <= n; i++)f[i][i + n] = 1;for (int i = 1; i <= n; i++) {for (int j = i; j <= n; j++)if (f[j][i]) {for (int k = 1; k <= nn; k++)swap(f[i][k], f[j][k]);break;}if (!f[i][i]) {return 0;}ll inv, y;exgcd(f[i][i], mod, inv, y);inv = (inv % mod + mod) % mod;for (int j = i; j <= nn; j++)f[i][j] = f[i][j] * inv % mod;for (int j = 1; j <= n; j++)if (j != i) {y = f[j][i];for (int k = i; k <= nn; k++)f[j][k] = (f[j][k] - y * f[i][k] % mod + mod) % mod;}}return 1;}
} m;

0x18. 矩阵的相似

0x19. 矩阵的特征值与特征向量

0x1A. Problem I

一个培养皿分为N个区域
一开始第1个区域有一个细胞
在每个单位时间,第i个区域的细胞,每个细胞会分裂成为K个细胞,且进入第j个区域的细胞有AijA_{ij}Aij​个。
细胞只会进入区域编号大于等于当前区域编号的区域,即
∀i,∀j<i,Ai,j=0\forall i,\forall j<i,A_{i,j}=0∀i,∀j<i,Ai,j​=0
数据保证∀i∀jAi,j≠Ai,j\forall i \forall j\ A_{i,j} ≠A_{i,j}∀i∀j Ai,j​​=Ai,j​
求T个时刻后细胞的数目,答案模p输出
N<=100N<=100N<=100

Solution

直接矩阵乘法 O(N3logT)O(N^3logT)O(N3logT)
注意到是上三角矩阵,且对角元两两不同
特征值即为对角线上的N个元素
我们可以在O(N3)O(N^3)O(N3)时间内求出所有特征值对应的特征向量
利用矩阵的相似将A写成三个矩阵相乘的形式,只需要对对角的元素使用快速幂
时间复杂度O(N3)O(N^3)O(N3)
需要高精度时时间复杂度大大降低

0x20. 高斯消元法

高斯消元用于解线性方程组

线性方程组如下:

定理:增广矩阵R(Aˉ)R(\bar{A})R(Aˉ)经过初等行变换不改变最后线性方程组的解(增广矩阵就是系数矩阵并上常数矩阵)

高斯消元算法总结起来就是一句话:通过初等行变换把增广矩阵的线性方程组变为简化阶梯型矩阵的线性方程组求解算法。

具体的思想就是:对于每一个未知量xix_ixi​,找到一个xix_ixi​ 的系数非零,但是x1x_1x1​ ~ xi−1x_{i-1}xi−1​的系数都是零的方程,然后用初等行变换把其他方程的xix_ixi​的系数全部消成零。

齐次线性方程一定有零解

n=mn = mn=m时方程组

仅有零解 ⇔\Leftrightarrow⇔ D=∣A∣≠0D = |A| ≠0D=∣A∣​=0 ⇔\Leftrightarrow⇔ R(A)=nR(A) = nR(A)=n

有非零解 ⇔\Leftrightarrow⇔ D=∣A∣=0D = |A| =0D=∣A∣=0 ⇔\Leftrightarrow⇔ R(A)<nR(A) < nR(A)<n

非齐次方程

有唯一解 ⇔\Leftrightarrow⇔ R(A)=R(Aˉ)=r=nR(A) = R(\bar{A}) = r = nR(A)=R(Aˉ)=r=n

有无穷多解 ⇔\Leftrightarrow⇔ R(A)=R(Aˉ)=r<nR(A) = R(\bar{A}) = r < nR(A)=R(Aˉ)=r<n

无解 ⇔\Leftrightarrow⇔ R(A)≠R(Aˉ)R(A) ≠ R(\bar{A})R(A)​=R(Aˉ)

高斯消元 ⇔\Leftrightarrow⇔ 消成上三角矩阵

高斯-约旦消元 ⇔\Leftrightarrow⇔ 消成对角矩阵

0x21. 高斯消元解线性方程组


如果给定线性方程组存在唯一解,则输出共n行,其中第i行输出第i个未知数的解,结果保留两位小数。

如果给定线性方程组存在无数解,则输出“Infinite group solutions”。

如果给定线性方程组无解,则输出“No solution”。

步骤:

  • 增广矩阵行初等行变换为行最简形;
  • 还原线性方程组;
  • 求解第一个变量;
  • 补充自由未知量;
  • 列表示方程组通解。
const int N = 507, mod = 1e9 + 7;
const double eps = 1e-6;
int n, m;
double a[N][N];
int guass()
{int c;//当前最左边的一列int r;//当前最上面的一行for(c = 1, r = 1; c <= n; ++ c){int t = r;for(int i = r + 1; i <= n; ++ i){if(fabs(a[i][c]) > fabs(a[t][c]))t = i;}if(fabs(a[t][c]) < eps)//等于0就下一个continue;//换到第一行(最上面的一行)for(int i = c; i <= n + 1; ++ i)swap(a[t][i], a[r][i]);//把该行第一个数变成 1(该行每一列都/=第c列的值)for(int i = n + 1; i >= c; -- i)a[r][i] /= a[r][c];//中间所有列(所有项)全部消掉for(int i = r + 1; i <= n; ++ i)if(fabs(a[i][c]) > eps)for(int j = n + 1; j >= c; -- j)a[i][j] -= a[r][j] * a[i][c];r ++ ;}if(r <= n){for(int i = r; i <= n; ++ i)if(fabs(a[i][n + 1]) > eps)//非零,无解return 2;return 1;//无穷多组解}//回代//行最简形矩阵for(int i = n; i >= 1; -- i)for(int j = i + 1; j <= n + 1; ++ j)a[i][n + 1] -= a[j][n + 1] * a[i][j];    /* 只要保留第i行第i列的系数即可,所以从第i+1行开始一直消到第n行,因为只要结果,所以可以只消第i行第n+1列的数(b);就是上面消掉所有列的简化版,少了一个for循环*/  return 0;
}
int main()
{cin >> n;for(int i = 1; i <= n; ++ i)for(int j = 1; j <= n + 1; ++ j)cin >> a[i][j];int t = guass();if(t == 0){for(int i = 1; i <= n; ++ i)printf("%.2f\n", a[i][n + 1]);}else if(t == 1)puts("Infinite group solutions");else puts("No solution");return 0;
}

0x22 高斯-约旦消元法

约旦消元法的精度更好、代码更简单,没有回带的过程。

约旦消元法大致思路如下:

  • 1.选择一个尚未被选过的未知数作为主元,选择一个包含这个主元的方程。
  • 2.将这个方程主元的系数化为1。
  • 3.通过加减消元,消掉其它所有方程中的这个未知数。
  • 4.重复以上步骤,直到把每一行都变成只有一项有系数

我们用矩阵表示每一项系数以及结果

const int N = 5007;
const double eps = 1e-6;//
int n, m;
double a[N][N];int guass() {for(int i = 1; i <= n; ++ i) {//枚举列int maxx = i;//1.找for(int j = i + 1; j <= n; ++ j)
//第i列以前的未知量除了第i一行的所有行都已经都已经消成0了,不能再动了if(fabs(a[j][i]) > fabs(a[maxx][i]))maxx = j;//2.换for(int j = 1; j <= n + 1; ++ j)//找到的最大一行跟第i行换swap(a[i][j], a[maxx][j]);if(fabs(a[i][i]) < eps) return -1;//3.减for(int j = 1; j <= n; ++ j)
//把该未知量除第i行以外全部减成0(普通的高斯是第i行以下减为0,但是需要回代)if(j != i) {double tmp = a[j][i] / a[i][i];for(int k = i + 1; k <= n +1; ++ k) //只需要减第i列右边就行了,因为左边全为0a[j][k] -= a[i][k] * tmp;}                               //高斯 - 约旦 消元法}                                       //最后会得到这样的矩阵for(int i = 1; i <= n; ++ i)            //    k1*a=e1a[i][n + 1] /= a[i][i];             //    k2*b=e2//所以最后要除以该项的系数     <--        //    k3*c=e3return 1;                               //    k4*d=e4
}int main()
{scanf("%d", &n);for(int i = 1; i <= n; ++ i)for(int j = 1; j <= n + 1; ++ j)scanf("%lf", &a[i][j]);int ans = guass();if(ans == -1) puts("No Solution");else for(int i = 1; i <= n; ++ i)printf("%.2f\n", a[i][n + 1]);return 0;
}

方程组无解:

0*x1+0*x2+...0*xi+...0*xn=a(a!=0)

方程组无穷解

0*x1+0*x2+...0*xi+...0*xn=a(a==0)

所以对所有行在处理完后,如果
Matrix[i][i]==0&&Matrix[i][n+1]!=0\tt Matrix[i][i]==0\ \&\&\ Matrix[i][n+1]!=0Matrix[i][i]==0 && Matrix[i][n+1]!=0 就是无解
Matrix[i][i]==0&&Matrix[i][n+1]==0\tt Matrix[i][i]==0\ \&\&\ Matrix[i][n+1]==0Matrix[i][i]==0 && Matrix[i][n+1]==0就是无穷解。
所以我们就可以在我们上面的程序的最后加一段用于特判的代码

for(int i = 1;i <= n; ++ i) {if(fabs(a[i][i]) < eps)if(fabs(a[i][n + 1] > eps)) {flag = 2; //无解break;}else flag = 1; //无穷多组解else f[i] = a[i][n + 1] / a[i][i];if(flag == 1)return cout << "1" << endl, 0;if(flag == 2)return cout << "2" <<endl, 0;
}

需要注意的是,我们使用约旦消元法的时候,交换如果出题人不小心()出了一组数据,所以如果出题人不小心()出了一组SB数据,约旦消元法是会被卡掉的,虽然我们可以使用随机算法来有概率地解决这个问题,但是还是很不爽,所以尽量少使用这个算法…一般尽量使用上面的算法

0x23. 高斯消元解异或线性方程组

输入一个包含n个方程n个未知数的异或线性方程组。

方程组中的系数和常数为0或1,每个未知数的取值也为0或1。

求解这个方程组。

异或线性方程组示例如下:

M[1][1]x[1] ^ M[1][2]x[2] ^ … ^ M[1][n]x[n] = B[1]
M[2][1]x[1] ^ M[2][2]x[2] ^ … ^ M[2][n]x[n] = B[2]
…
M[n][1]x[1] ^ M[n][2]x[2] ^ … ^ M[n][n]x[n] = B[n]

其中 ^ 表示异或 (XOR)(XOR)(XOR) ,M[i][j]M[i][j]M[i][j]表示第 iii 个式子中 x[j]x[j]x[j] 的系数, B[i]B[i]B[i] 是第 iii 个方程右端的常数,取值均为 000 或 111 。

异或 ⇔\Leftrightarrow⇔ 不进位的二进制加法
也就是说异或运算只会影响这一位,不会影响其他位的值

对于 k=1...nk=1...nk=1...n,找到一个 a[i][k]a[i][k]a[i][k] 不为 0 的行 iii,把它与第 kkk 行交换,用第 kkk 行去异或下面所有 a[i][j]a[i][j]a[i][j] 不为
000 的行 iii ,消去它们的第 kkk 个系数,这样就将原矩阵化成了上三角矩阵

由于最后一行只有一个未知数,这个未知数就已经求出来了,然后用它跟上面所有含有这个未知数的方程异或,以此类推即可以自下而上求出所有未知数。

要注意的是,对于开关问题,a[i][j]=1a[i][j]=1a[i][j]=1 表示第 iii 个开关受第 jjj 个开关影响

使用 bitset 优化,时间复杂度为O(n332)O(\frac{n^3}{32})O(32n3​)。

const int N = 107, M = 500007, INF = 0x3f3f3f3f;
int n, m;
bitset<120> a[N];
int guass(int n, int m)//n row n 行 m col m 列
{int row = 0, col = 0, maxx;for(; col < m; ++ col) {for(maxx = row; maxx < n; ++ maxx)if(a[maxx][col])break;if(maxx == n) continue;if(a[maxx][col] == 0) continue;swap(a[maxx], a[row]);for(int i = row + 1; i < n; ++ i) if(a[i][col]) //如果这一列有数的话那一行全部消掉a[i] = a[i] ^ a[row];//for(int j = n; j >= col; -- j)//col前面都是0,无所谓//a[i][j] = a[i][j] ^ a[row][j];row ++ ;}if(row < n) {for(int i = row; i < n; ++ i) if(a[i][m])//矛盾,出现非零的常数项等于0,说明无解return 2;return 1;//有无穷多组解}//行最简形矩阵,第i行第i列的表示的未知量x_i的一个解for(int i = n - 1; i >= 0; -- i) {//第i行,第i行第i列for(int j = i + 1; j < m; ++ j)//第j列,右边所有列都要消a[i][m] = a[i][m] ^ (a[j][m] * a[i][j]);}return 0;
}int x;itn main()
{scanf("%d", &n);m = n;for(int i = 0; i < n; ++ i) for(int j = 0; j < m + 1; ++ j) //+1是因为多了一个常数矩阵(也即是答案)scanf("%d", &x), a[i][j] = x;int res = guass(n, m);if(res == 0) {for(int i = 0; i < n; ++ i) x = a[i][m], printf("%d\n", x);}else if(res == 2) puts("No solution");else puts("Multiple sets of solutions");return 0;
}

0x24. 高斯消元解模线性方程组

在解模线性方程组组时,当有唯一解时,需要对每个方程的唯一解不断的循环取模判断,直到找出能整除的为止

int a[N][N];//增广矩阵
int x[N];//解集
bool freeX[N];//标记是否为自由变元
int GCD(int a, int b) {return !b ? a : GCD(b, a % b);}
int LCM(int a, int b) {return a / GCD(a, b) * b;}
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) //与第row行交换for (int j = row; j < var + 1; j++)swap(a[row][j], a[maxRow][j]);if (a[row][col] == 0) { //col列第row行以下全是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) % MOD + MOD) % MOD;}}/*求解*///无解:化简的增广阵中存在(0,0,...,a)这样的行,且a!=0for (int i = row; i < equ; i++)if (a[i][col] != 0)return -1;//无穷解: 在var*(var+1)的增广阵中出现(0,0,...,0)这样的行int temp = var - row; //自由变元有var-row个if (row < var) //返回自由变元数return temp;//唯一解: 在var*(var+1)的增广阵中形成严格的上三角阵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];temp = (temp % MOD + MOD) % MOD; //取模}while (temp % a[i][i] != 0) //外层每次循环都是求a[i][i],它是每个方程中唯一一个未知的变量temp += MOD; //a[i][i]必须为整数,加上周期MODx[i] = (temp / a[i][i]) % MOD; //取模}return 0;
}

0x25. 矩阵求逆

【模板】矩阵求逆

求一个 N×NN\times NN×N 的矩阵的逆矩阵。答案对 109+710^9+7109+7 取模。

学过线代的都知道
[A,B]⇔[E,A−1B]↔[A,E]⇔[E,A−1][A,B] \Leftrightarrow[E,A^{-1}B ]\leftrightarrow[A,E] \Leftrightarrow[E,A^{-1}][A,B]⇔[E,A−1B]↔[A,E]⇔[E,A−1]
所以我们想要求矩阵的逆,只需要将矩阵和一个单位矩阵合并,然后使用高斯消元即可,最后得到的右边的矩阵就是矩阵的逆

代码:

0x26. 行列式计算

N×MN \times MN×M方阵行列式(Determinant)可以理解为所有列向量所夹的几何体的有向体积
例如:

[1001]=1\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} = 1[10​01​]=1

∣1221∣=−3\begin{vmatrix} 1 & 2 \\ 2 & 1 \end{vmatrix} = -3∣∣∣∣​12​21​∣∣∣∣​=−3

行列式有公式

D=∣A∣=∑(−1)va1,l1a2,l2…an,lnD = |A| = \sum(-1)^va_{1,l_1}a_{2,l_2} \dots a_{n,l_n}D=∣A∣=∑(−1)va1,l1​​a2,l2​​…an,ln​​

其中vvv 为 l1,l2…lnl_1,l_2 \dots l_nl1​,l2​…ln​中逆序对的个数。

通过体积概念理解行列式不变性是一个非常简单的办法:

  • 矩阵转置,行列式不变;
  • 矩阵行(列)交换,行列式取反;
  • 矩阵行(列)相加或相减,行列式不变;
  • 矩阵行(列)所有元素同时乘以数 ,行列式等比例变大。

由此,对矩阵应用高斯消元之后,我们可以得到一个对角线矩阵,此矩阵的行列式由对角线元素之积所决定。其符号可由交换行的数量来确定(如果为奇数,则行列式的符号应颠倒)。因此,我们可以在O(n3)O(n^3)O(n3)的复杂度下使用高斯算法计算矩阵。
注意,如果在某个时候,我们在当前列中找不到非零单元,则算法应停止并返回 0。

const double EPS = 1E-9;
int n;
vector<vector<double>> a(n, vector<double>(n));
double det = 1;
for (int i = 0; i < n; ++i) {int k = i;for (int j = i + 1; j < n; ++j)if (abs(a[j][i]) > abs(a[k][i]))k = j;if (abs(a[k][i]) < EPS) {det = 0;break;}swap(a[i], a[k]);if (i != k)det = -det;det *= a[i][i];for (int j = i + 1; j < n; ++j)a[j][i] /= a[i][i];for (int j = 0; j < n; ++j)if (j != i && abs(a[j][i]) > EPS)for (int k = i + 1; k < n; ++k)a[j][k] -= a[i][k] * a[j][i];
}
cout << det;

0x27. 生成树计数

一个无向图的生成树个数为邻接矩阵度数矩阵去一行一列的行列式。

详见: 矩阵树定理

例如,一个正方形图的生成树个数


∣−2101−2101−2∣=4\begin{vmatrix} -2 & 1 & 0 \\ 1 & -2 & 1\\ 0 & 1 & -2 \end{vmatrix} = 4∣∣∣∣∣∣​−210​1−21​01−2​∣∣∣∣∣∣​=4

可以用高斯消元解决,时间复杂度为 O(n3)O(n^3)O(n3)。

#define MOD 100000007
#define eps 1e-7
struct matrix {static const int maxn = 20;int n, m;double mat[maxn][maxn];matrix() {memset(mat, 0, sizeof(mat));}void print() {cout << "MATRIX " << n << " " << m << endl;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) cout << mat[i][j] << "\t";cout << endl;}}void random(int n) {this->n = n;this->m = n;for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)mat[i][j] = rand() % 100;}void initSquare() {this->n = 4;this->m = 4;memset(mat, 0, sizeof(mat));mat[0][1] = mat[0][3] = 1;mat[1][0] = mat[1][2] = 1;mat[2][1] = mat[2][3] = 1;mat[3][0] = mat[3][2] = 1;mat[0][0] = mat[1][1] = mat[2][2] = mat[3][3] = -2;this->n--;  // 去一行this->m--;  // 去一列}double gauss() {double ans = 1;for (int i = 0; i < n; i++) {int sid = -1;for (int j = i; j < n; j++)if (abs(mat[j][i]) > eps) {sid = j;break;}if (sid == -1)continue;if (sid != i) {for (int j = 0; j < n; j++) {swap(mat[sid][j], mat[i][j]);ans = -ans;}}for (int j = i + 1; j < n; j++) {double ratio = mat[j][i] / mat[i][i];for (int k = 0; k < n; k++) mat[j][k] -= mat[i][k] * ratio;}}for (int i = 0; i < n; i++)ans *= mat[i][i];return abs(ans);}
};
int main() {srand(1);matrix T;// T.random(2);T.initSquare();T.print();double ans = T.gauss();T.print();cout << ans << endl;
}

0x30. 线性空间

0x31. 线性空间

线性空间是一个关于向量加法和向量数乘的两个运算封闭的向量集合

若给定若干个向量a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​,若向量bbb 能由a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​经过向量加法和数乘运算得出,则称向量 bbb 能被向量 a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​ 表出。显然,a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​ 能表出的所有向量构成一个线性空间,a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​被称为这个线性空间的生成子集,这个线性空间被称为 a1,a2,…,aka_1,a_2,\dots,a_ka1​,a2​,…,ak​ 的子空间。

任意选出线性空间的若干个向量,如果其中存在一个向量能被其他是向量线性表出,则称这些向量线性相关,否则称这些向量线性无关。

线性无关的生成子集被称为线性空间的基底,简称为。基的另一种定义是线性空间的极大线性无关子集,一个线性空间的所有的基包含的向量个数相等,这个数被称作线性空间的维数

0x32. 线性相关

n维线性空间:将其看做一个集合,其中的每一个元素均使用n个相互独立的自由变量表示,如坐标系可看做二维线性空间

线性组合:设α₁,α₂,…,αₑ(e≥1)α₁,α₂,…,αₑ(e≥1)α₁,α₂,…,αₑ(e≥1)是域 PPP 上线性空间 VVV 中的有限个向量.若VVV中向量 ααα 可以表示为:α=k₁α₁+k₂α₂+…+kₑαₑ(kₑ∈P,e=1,2,…,s)α=k₁α₁+k₂α₂+…+kₑαₑ(kₑ∈P,e=1,2,…,s)α=k₁α₁+k₂α₂+…+kₑαₑ(kₑ∈P,e=1,2,…,s),则称α是向量组α₁,α₂,…,αₑα₁,α₂,…,αₑα₁,α₂,…,αₑ 的一个线性组合,亦称 ααα 可由向量组 α₁,α₂,…,αₑα₁,α₂,…,αₑα₁,α₂,…,αₑ 线性表示或线性表出

线性相关性

对于一个 nnn 维向量元素集合S(a1,a2…,am)S(a_1,a_2…,a_m )S(a1​,a2​…,am​),若存在等式a1k1×a2k2…×amkm=0(k1…kma_1k_1\times a_2k_2… \times a_m k_m=0(k_1…k_ma1​k1​×a2​k2​…×am​km​=0(k1​…km​不全为 0)0)0),则称 AAA 线性相关,反之线性无关

等价向量组

对于向量组A:a1,a2,…,am、B:b1,b2,…,bnA:a_1,a_2,…,a_m、B:b_1,b_2,…,b_nA:a1​,a2​,…,am​、B:b1​,b2​,…,bn​,如果 AAA 能被 BBB 线性表示且 BBB 能被 AAA 线性表示,则向量组 A、BA、BA、B 等价

等价向量组定理:若 AAA 可由 BBB 线性表示且 m>nm>nm>n,则 AAA 线性相关

推论1:若 AAA 可由 BBB 线性表示且m≤nm \le nm≤n,则 AAA 线性无关

推论2:两个线性无关的等价向量组,包含个数相同的向量

0x33. 极大线性无关组

对于向量组A:a1…ana_1…a_na1​…an​ ,若存在一组 ai…ar(r−i<n)a_i \dots a_r(r-i<n)ai​…ar​(r−i<n) 线性无关且 ai…ar+1a_i…a_{r+1}ai​…ar+1​ 线性相关,则称 ai…ara_i…a_rai​…ar​ 为 AAA 的一组极大线性无关组,通俗的说,极大线性无关组 ai…ara_i…a_rai​…ar​ 能表示向量组 AAA 中的其它任意元素且本身线性无关

  • 若A线性无关,则其极大线性无关组就是其本身

极大无关组性质:

  • 向量组A的任意极大线性无关组与A等价

  • 向量组A的任意两个极大线性无关组等价

推论:一个向量组A的任意两个极大线性无关组等价等大

0x34. 向量组的秩

AAA 的极大线性无关组的大小称为向量组 AAA 的秩,记作 R(A)R(A)R(A)

性质:若向量组 AAA 可由向量组 BBB 线性表示,则R(A)≤R(B)R(A) \le R(B)R(A)≤R(B)

0x35. 线性基

220≈1062^{20} ≈10^{6}220≈106
224≈1072^{24} ≈10^{7}224≈107
227≈1082^{27} ≈10^{8}227≈108
230≈1092^{30} ≈10^{9}230≈109
234≈10102^{34} ≈10^{10}234≈1010
260≈10182^{60} ≈10^{18}260≈1018
上述的约等于均是大于(往下看就知道有什么用了)

0x35.1 前置知识

异或和

设 SSS 为一无符号整数集(下文中除非特殊说明,集合均指「无符号整数集」),定义其异或和 xor-sum(S)=S1xorS2xor…xorSS\text{xor-sum}(S) = S_1 \mathbin{\text{xor}} S_2 \mathbin{\text{xor}} \ldots \mathbin{\text{xor}} S_{S}xor-sum(S)=S1​xorS2​xor…xorSS​。

张成

设 T⊆ST \subseteq ST⊆S,所有这样的子集 TTT 的异或和组成的集合称为集合 SSS 的张成,记作 span(S)\text{span}(S)span(S) 。即,在 SSS 中选出任意多个数,其异或和的所有可能的结果组成的集合

线性相关

对于一个集合 SSS ,如果存在一个元素 SjS_jSj​ ,使得,SSS 在去除这个元素后得到的集合 S′S'S′ 的张成 span(S)\text{span}(S)span(S) 中包含 SjS_jSj​ ,即,Sj∈span(S′)S_j \in \text{span}(S')Sj​∈span(S′),则称集合 SSS 线性相关。

更形象地,可以表示为,存在一个元素 SjS_jSj​,可以用其它若干个元素异或起来得到。

相对的,如果不存在这样的元素 SjS_jSj​,则称集合 SSS 线性无关

一个显然的结论是,对于这个线性相关的集合 SSS ,去除这个元素后,集合的张成不变。

线性基

线性基是向量空间的一组基。

我们称集合 BBB 是集合 SSS 的线性基,当且仅当:

  • S⊆span(B)S \subseteq \text{span}(B)S⊆span(B),即 集合SSS 是 集合BBB 的张成的子集;
  • 集合BBB是线性无关的。

集合 BBB 中元素的个数,称为线性基的长度,也就是该线性空间的维数。

线性基有以下基本性质:

  • BBB是极小的满足线性基性质的集合,它的任何真子集都不可能是线性基;
  • SSS 中的任意元素都可以唯一表示为 BBB 中若干个元素异或起来的结果。
  • 线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值,并且线性基是满足该性质的最小的集合。
  • 线性基没有异或和为 0 的子集。
  • 线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。(因为前面也说了,基是极大线性无关向量组)
  • 线性基中每个元素的二进制最高位互不相同

那么如何求得线性基呢?

0x35.2 求线性基

两个题目

  • 查询最大异或和

题目大意: 给定n个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。n≤105,0≤si≤263−1n \le 10^5,0 \le s_i \le 2^{63}-1n≤105,0≤si​≤263−1

  • 求第 kkk 小异或和

给定你由N个整数构成的整数序列,你可以从中选取一些(至少一个)进行异或(XOR)运算,从而得到很多不同的结果。

请问,所有能得到的不同的结果中第k小的结果是多少。n≤104,k≤1018n \le 10^4,k \le 10^{18}n≤104,k≤1018。

考虑将 nnn 个数在二进制下看成 nnn 个向量,每个向量中的每个元素仅有0、10、10、1两种取值。

如果向量 XXX 能被 a1,a2..ana_1,a_2..a_na1​,a2​..an​ 这些向量通过异或运算得到,则称 XXX 能被 a1,a2..ana_1,a_2..a_na1​,a2​..an​​ 表出。如果某个向量集合中存在若干个向量能被其它向量表出,则这些向量线性相关,否则线性无关

定义一个向量集合的线性基为其极大线性无关子集(可能会有多个线性基),显然线性基可以表出这个向量集合

线性异或空间是对异或运算封闭的向量集合。

异或高斯消元仅有向量间的异或操作,所以消元不会改变这些向量能表出的线性空间。因此,高斯消元可以求得一组线性基。

我们用高斯消元求出的a1,a2,…,ana_1,a_2, \dots,a_na1​,a2​,…,an​构成的异或空间的基,我们设这个基有整数b1,b2,…,btb_1,b_2, \dots,b_tb1​,b2​,…,bt​构成,其中b1>b2>⋯>btb_1>b_2>\dots>b_tb1​>b2​>⋯>bt​,从基底中选取若干个整数,显然有2t2^t2t种选法,因为选或者不选,每个数两种取法,因此这个 ttt 维线性异或空间一共有 2t2^t2t 个整数,与这 2t2^t2t 种取法一一对应。(这里是关键)

在上面的高斯消元的过程中,一共有 ttt 个主元,分别为 b1,b2,…,btb_1,b_2, \dots,b_tb1​,b2​,…,bt​ 的最高位,假设它们的最高位分别是第 c1,c2,…,ctc_1,c_2, \dots,c_tc1​,c2​,…,ct​,那么显然 c1>c2>⋯>ctc_1>c_2>\dots>c_tc1​>c2​>⋯>ct​。

由高斯消元得到的线性基一定是行最简形型矩阵,即在阶梯形矩阵中,若非零行的第一个非零元素全是1,且非零行的第一个元素1所在列的其余元素全为零,就称该矩阵为行最简形矩阵。

例如整数5,12,2,7,95,12,2,7,95,12,2,7,9,写成的 010101 矩阵,以及高斯消元后得到的简化阶梯型矩阵如下:

logs 列 n 行(s是数组最大值,n是数组个数)
0 1 0 1    1 0 0 1
1 1 0 0    0 1 0 1
0 0 1 0 => 0 0 1 0
0 1 1 1    0 0 0 0
1 0 0 1    0 0 0 0

异或空间的基是9,5,29,5,29,5,2,最高位1分别在第333位,第222位,第111位,自由元是第000位,可以发现,只有9的第3位是1,5和2的第3位都是0,即满足主元所在位为 111 的整数是唯一的。

因为基底 b1,b2,…,btb_1,b_2, \dots,b_tb1​,b2​,…,bt​ 能与 a1,a2,…,ana_1,a_2, \dots,a_na1​,a2​,…,an​线性表出相同的线性异或空间,所以异或空间种第 kkk 小的整数,就是从基底 b1,b2,…,btb_1,b_2, \dots,b_tb1​,b2​,…,bt​ 中选出若干个数做异或运算,能线性表出的第 kkk 小的整数,也就是整个线性空间里第 kkk 小的整数。而第 cic_ici​ 位是 111 的只有 b1b_1b1​,所以选择 b1b_1b1​ 一定比不选 b1b_1b1​ 所线性表示出来的数更大,同理可得,整个线性异或空间中最大的数就是b1>b2>⋯>btb_1>b_2>\dots>b_tb1​>b2​>⋯>bt​全部异或起来得到的数,也就是从 nnn 个数中选若干个数字能构成的最大的异或和。

所以我们的答案一定是主对角线上的元素一全部异或在一起是最优的。

根据上面的性质,nnn个整数在二进制下组成的向量组的线性空间的基的维数只有636363,因为二进制下si≤263−1s_i \le 2^{63}-1si​≤263−1,最多有626262位,也就是说最多有636363 个二进制位数,所以线性基包含的向量最多只有636363个,所以整体的时间复杂度为O(nlogs)O(nlogs)O(nlogs)。

第一题代码:

const int N = 200007, mod = 1e9 + 7, INF = 2.1e9;
const double eps = 1e-8;
int n, m;
ll a[N];
int main()
{scanf("%d", &n);for(int i = 0; i < n; ++ i) scanf("%lld", &a[i]);int k = 0;//每个数就是一个二进制的向量//先枚举列,也就是二进制,最多62位//做一次高斯消元for(int i = 62; i >= 0; -- i) {for(int j = k; j < n; ++ j) if(a[j] >> i & 1){//第i位是1swap(a[j], a[k]);//相当于整个向量交换break;}if(!(a[k] >> i & 1)) //如果这一位全是0,就跳过continue;for(int j = 0; j < n; ++ j)if(j != k && (a[j] >> i & 1))a[j] ^= a[k];//垄断,除了找到的k以外全部变成0k ++ ;if(k == n) break;}ll res = 0;for(int i = 0; i < k; ++ i) //最后上三角形的主对角线的k个数异或起来就是答案,因为每一个数都要选才会最优res ^= a[i];printf("%lld\n", res);return 0;
}

继续考虑第二题

题目中不允许出现aixor aia_i\ \text{xor}\ a_iai​ xor ai​,也就是不一定能线性表出 000,而普通的线性空间是允许的,导致普通线性空间一定存在整数 000,而从a1,a2,a3…,ana_1,a_2,a_3 \dots ,a_na1​,a2​,a3​…,an​中选出不同的数异或起来,可能得不到 000。

我们这里设base\tt basebase是线性基的维数
当 base<n\tt base<nbase<n时,则向量组里这 nnn 个向量线性相关,一定可以构成0。
因为若线性相关,一定存在

xi=a1x1+a2x2+…anxnx_i=a_1x_1+a_2x_2 + \dots a_nx_nxi​=a1​x1​+a2​x2​+…an​xn​

0=a1x1+a2x2+…anxn−xi0=a_1x_1+a_2x_2 + \dots a_nx_n-x_i0=a1​x1​+a2​x2​+…an​xn​−xi​
即可以线性表出整数 000。

若 base=n\tt base =nbase=n,则则向量组里这 nnn 个向量线性无关,一定不能线性表出整数 000。

因为若可以表出 000,则一定存在

0=a1x1+a2x2+…anxn0=a_1x_1+a_2x_2+ \dots a_nx_n0=a1​x1​+a2​x2​+…an​xn​

若a1a_1a1​非零,则

−a1x1=a2x2+…anxn-a_1x_1=a_2x_2+ \dots a_nx_n−a1​x1​=a2​x2​+…an​xn​

x1=−a2a1x2−⋯−ana1xnx_1=-\frac{a_2}{a_1}x_2- \dots -\frac{a_n}{a_1}x_nx1​=−a1​a2​​x2​−⋯−a1​an​​xn​

存在 x1x_1x1​ 可以由向量组的其他向量线性表出,说明向量组一定线性相关,与前提条件矛盾,故当向量组线性无关,即 base=n\tt base =nbase=n 时,不能线性表出 000。

所以综上所诉,如果能得到 000 的话,就把 k−1k-1k−1 进行二进制分解,减掉 111 是为了把最小的数定义为第 000 小(因为我们二进制有第 000 位),题目中是第 111 小,如果k−1k-1k−1的第j(0≤j≤t)j(0 \le j \le t)j(0≤j≤t)位等于1,就选取 bjb_{j}bj​ ,也就是从左往右数第 jjj 个(二进制是从右往左数,所以我们重新从小到大,从右往左循环vector存一下 bbb 数组999),最后把所有选出的 bbb 异或起来,就是答案。特别地,若 k>2tk > 2^tk>2t,则无解。

这样选的原因是因为上面说了,如果我们线性基有 ttt 个向量,那么就会有 2t2^t2t 个取法,也就是说我们要找的第 kkk 小的数和上面 2t2^t2t 个数一一对应,因为线性基的b1,b2,…,btb_1,b_2, \dots,b_tb1​,b2​,…,bt​ 已经从大到小排过序了,所以同样的顺序选取即可,根据二进制的性质,正确性显然。

如果不能线性表出0的话就把 kkk 进行二进制分解,而不是 k−1k-1k−1 ,相当于统计的时候把 000 跳过去,因为此时应该没有 000,但是线性基中一定有 000,所以我们假装看不见它。

啊,感觉说的有点乱…

ll query_kth(ll k, int n) {ll ans = 0;if(zero) k -- ;if(k >= (1ll << (int)mmap.size()))return -1;for(int i = 0; i < (int)mmap.size(); ++ i)if((k >> i) & 1)ans ^= mmap[i];return ans;}
};

0x35.3 线段树套线性基

给你 nnn 和 nnn 个整数的数组 aaa,以及kkk和qqq,有 qqq 次询问,每次询问输入两个整数l,rl,rl,r,表示区间[l,r][l,r][l,r],要你每次在这个区间里选择若干个数,使得这些数的异或和sumsumsum,再按位或kkk的值最大,即求最大的res = sum | k。详细题解(2017年ICPC西安邀请赛A)

将 kkk 取反,然后把所有数在加入线性基之前,全部 按位异或运算一遍,这样把没用的1全部删掉,再加入线性基。最终的答案就是 k | sumsum 是修改后的区间的数插入线性基的最大异或和)。我们使用线段树维护区间操作即可。实际上就是把线段树权值换成当前区间的线性基,简单维护一下即可。

const int N = 50007, mod = 1e9 + 7, INF = 2.1e9;
const double eps = 1e-8;
const int Base = 27 * 2;//线性基数组大小,开大一点,嘻嘻嘻
struct LinearBase {int dimension = 27; // dimension 维数,就是线性基的维数 = logs, (s为元素最大值)//dimension一定要是logs,错一点就会WA呜呜呜//线性基数组ll a[Base + 7];LinearBase() {fill(a, a + Base + 7, 0);}LinearBase(ll *x, int n) {build(x, n);}void insert(ll t){// 逆序枚举二进制位for(int i = dimension; i >= 0; -- i) {if(t == 0) return ;// 如果 t 的第 i 位为 0,则跳过if(!(t >> i & 1)) continue;// 如果 a[i] != 0,则用 a[i] 消去 t 的第 i 位上的 1if(a[i]) t ^= a[i];else {// 找到可以插入 a[i] 的位置for(int k = 0; k < i; ++ k)if(t >> k & 1) t ^= a[k];// 用 t 消去 a[i + 1...L] 的第 i 位上的 1for(int k = i + 1; k <= dimension; ++ k)if(a[k] >> i & 1) a[k] ^= t;// 插入到 a[j] 的位置上a[i] = t;break;}// 此时 t 的第 i 位为 0,继续寻找其最高位上的 1          }// 如果没有插入到任何一个位置上,则表明 t 可以由 a 中若干个元素的异或和表示出,即 t 在 span(a) 中}// 数组 x 表示集合 S,下标范围 [1...n]void build(ll *x, int n){fill(a, a + Base + 7, 0);for(int i = 1; i <= n; ++ i)insert(x[i]);}ll query_max(){ll res = 0;for(int i = 0; i <= dimension; ++ i)res ^= a[i];return res;}void mergefrom(const LinearBase &other) {for(int i = 0; i <= dimension; ++ i)insert(other.a[i]);}static LinearBase merge(const LinearBase &a, const LinearBase &b) {LinearBase res = a;for(int i = 0; i <= 27; ++ i)res.insert(b.a[i]);return res;}
};
int n, m, t, q, k;
ll a[N];struct Tree {int l, r;LinearBase elem;//element
}tr[N];void build(int p, int l, int r)
{tr[p] = {l, r};if(l == r) {tr[p].elem.insert(a[r]);return ;}int mid = (l + r) >> 1;build(p << 1, l, mid);build(p << 1 | 1, mid + 1, r);tr[p].elem = tr[p].elem.merge(tr[p << 1].elem, tr[p << 1 | 1].elem);
}LinearBase query(int p, int l, int r)
{if(l <= tr[p].l && tr[p].r <= r)return tr[p].elem;int mid = (tr[p].l + tr[p].r) >> 1;LinearBase res;if(l <= mid) res = res.merge(res, query(p << 1, l, r));if(r > mid) res = res.merge(res, query(p << 1 | 1, l, r));return res;
}int main()
{scanf("%d", &t);while(t -- ) {scanf("%d%d%d", &n, &q, &k);for(int i = 1; i <= n; ++ i) {scanf("%lld", &a[i]);/*k的存在会对求线性基最大值时的主元产生影响,所以预处理一下,a[i]只保留k为0的位,这样贡献最大*/for(int j = 0; j < 27; ++ j) {if((k >> j & 1) && (a[i] >> j & 1))a[i] ^= (1 << j);}}build(1, 1, n);while(q -- ) {int l, r;scanf("%d%d", &l, &r);LinearBase res = query(1, l, r);printf("%lld\n", res.query_max() | k);}}return 0;
}

0x35.6 线性基全家桶(code)

支持操作:

  • 合并

两个集合的线性基可以在 O(log2t)O(log^2t)O(log2t)的时间内进行合并,合并后得到的线性基为两个集合的并的线性基。
合并只需要将一个线性基中的所有元素插入到另一个线性基中即可。

  • 求最大异或和
  • 求最小异或和
  • 求第k小值
  • 检查某数是否能被线性表出中
    (即是否在线性空间中)
  • 查询某数在线性空间中的排名
  • 线性基中动态插入一个数
  • 合并两个线性基
    以上操作均为 O(logs)O(logs)O(logs)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <queue>
using namespace std;
typedef long long ll;
typedef int itn;
typedef pair<int, int>PII;
const int N = 200007, mod = 1e9 + 7, INF = 2.1e9;
const double eps = 1e-8;
const int Base = 60 * 2;//线性基数组大小,开大一点,嘻嘻嘻int base_cnt;
bool zero;struct LinearBase
{int dimension = 60; // dimension 维数,就是线性基的维数 = logs, (s为元素最大值)//dimension一定要是logs,错一点就会WA呜呜呜//线性基数组ll a[Base + 7];//注意这里要加一点,因为下面的循环也是这个数vector<ll> mmap;LinearBase() {/*构造方法*///for(int i = 0; i <= Base; ++ i)//    a[i] = 0;fill(a, a + Base + 7, 0);}LinearBase(ll *x, int n) {build(x, n);}void insert(ll t)/*插入一个数*/{// 逆序枚举二进制位for(int i = dimension; i >= 0; -- i) {//if(t == 0) return ;// 如果 t 的第 i 位为 0,则跳过if(!(t >> i & 1)) continue;// 如果 a[i] != 0,则用 a[i] 消去 t 的第 i 位上的 1if(a[i]) t ^= a[i];else {// 插入到 a[i] 的位置上// 找到可以插入 a[i] 的位置// 用 a[0...i - 1] 消去 t 的第 [0, i) 位上的 1// 如果某一个 a[k] = 0 也无须担心,因为这时候第 k 位//不存在于线性基中,不需要保证 t 的第 k 位为 0for(int k = 0; k < i; ++ k)if(a[k] && (t >> k & 1)) t ^= a[k];// 用 t 消去 a[i + 1...L] 的第 i 位上的 1for(int k = i + 1; k <= dimension; ++ k)if(a[k] >> i & 1) a[k] ^= t;a[i] = t;base_cnt ++ ;break;}// 此时 t 的第 i 位为 0,继续寻找其最高位上的 1}// 如果没有插入到任何一个位置上,则表明 t 可以由 a 中若干个元素的异或和表示出,即 t 在 span(a) 中}// 数组 x 表示集合 S,下标范围 [1...n]void build(ll *x, int n)/*求整个数组(向量组)的线性基*/{base_cnt = 0;zero = 0;fill(a, a + Base + 7, 0);for(int i = 1; i <= n; ++ i)insert(x[i]);zero = base_cnt != n;/*是否有零行,表示是否能够线性表出0*/mmap.clear();for(int i = 0; i <= dimension; ++ i)if(a[i])mmap.push_back(a[i]);//逆序从第0位存到第t位,这样下标可以直接对应,比较方便}ll query_max()/*求最大异或和*/{ll res = 0;for(int i = 0; i <= dimension; ++ i)res ^= a[i];return res;}ll query_min(int n)/*求最小异或和,至少选择一个数*/{zero = base_cnt != n;if(zero) return 0;for(int i = 0; i <= dimension; ++ i)if(mmap[i])return mmap[i];}bool check_in(ll x)/*check一个数是否在线性基中*/{for(int i = dimension; i >= 0; -- i)if(x >> i & 1)x ^= a[i];return x == 0;}void mergefrom(const LinearBase &other) {/*一个线性基与另一个线性基合并*/for(int i = 0; i <= dimension; ++ i)insert(other.a[i]);}static LinearBase merge(const LinearBase &a, const LinearBase &b) {/*合并两个线性基,多用于线段树维护线性基*/LinearBase res = a;for(int i = 0; i <= 60; ++ i)res.insert(b.a[i]);return res;}ll query_kth(ll k, int n) {/*查询线性空间中第k小的值*//*细节颇多,注意zero的特判*//*这里别忘了在主函数里初始化base_cnt = 00以及zero = 0,已在build函数中初始化,所以数组尽量直接用build*/ll ans = 0;if(zero) k -- ;if(k >= (1ll << (int)mmap.size()))return -1;for(int i = 0; i < (int)mmap.size(); ++ i)if((k >> i) & 1)ans ^= mmap[i];return ans;}ll query_rank(ll x) {ll ans = 0;for(int i = 0; i <= (int)mmap.size(); ++ i)if(x >= mmap[i])ans += (1ll << i), x ^= mmap[i];return ans + zero;}
};//上述操作均通过简单测试
int n;
ll x;
ll cnt, t, q, k;
ll a[N];int main()
{scanf("%d", &t);while(t -- ) {scanf("%d", &n);for(ll i = 1; i <= n; ++ i)scanf("%lld", &a[i]);LinearBase Line(a, n);printf("Case #%d:\n", ++ cnt);scanf("%lld", &q);while(q -- ) {scanf("%lld", &k);printf("%lld\n", Line.query_kth(k, n));}}return 0;
}
  • 求两个线性基的交
  • 求两个线性基的并(通过容斥原理将并转化为交即可)

两个线性空间 V1V_1V1​​ 与 V2V_2V2​ ,求出这两个空间的交与并。当然OI中的线性空间大部分是指异或操作下的,也就是常说的线性基。
如下所说的,都是异或的线性基,不区分加号与异或,即∑xi=x1⊕x2...⊕x3\sum x_i = x_1 \oplus x_2 ... \oplus x_3∑xi​=x1​⊕x2​...⊕x3​ 。
当然可以扩展到任意线性空间,大致方法不变。

明显答案也是一个线性基。
那么我们还需要如下引理:
设 {αi=1−x}\{\alpha_{i= 1 - x}\}{αi=1−x​} 是 V1V_1V1​ 的一组线性基,{βi=1−y}\{\beta_{i= 1 - y}\}{βi=1−y​} 是 V2V_2V2​ 的一组线性基。设W WW是 {β}\{\beta\}{β} 中在 V1V_1V1​ 的基,如果α⋃{β∖W}\alpha \bigcup\{\beta \setminus{W}\}α⋃{β∖W} 线性无关,那么 WWW 就是交的线性基。
从而可以得到:

如果βi\beta_iβi​ 能被{αi=1−x}\{\alpha_{i=1-x}\}{αi=1−x​} 线性表示出来,我们可以把⨁i=1xαi\bigoplus_{i = 1}^{x}\alpha_i⨁i=1x​αi​ 或者 ⨁j=1i−1βj\bigoplus_{j=1}^{i-1}\beta_j⨁j=1i−1​βj​​
(注意要么加入 α\alphaα 要么加入 β\betaβ )加入答案的线性基中。
code:

LinearBasis Merge(LinearBasis A,LinearBasis B) {LinearBasis All , C , D;All.clear();C.clear();D.clear();for (int i = 60;i >= 0;i--) {All.basis[i] = A.basis[i];D.basis[i] = 1ll << i;}for (int i = 60; i >= 0; i--) {if (B.basis[i]) {ll v = B.basis[i] , k = 0;bool can = true;for (int j = 60; j >= 0; j--) {if (v & (1ll << j)) {if (All.basis[j]) {v ^= All.basis[j];k ^= D.basis[j];} else {can = false;All.basis[j] = v;D.basis[j] = k;break;}}}if (can) {ll v = 0;for (int j = 60; j >= 0; j--) {if (k & (1ll << j)) {v ^= A.basis[j];}}C.insert(v);}}}C.build();return C;
}

0x35.7 线性基总结

线性基的题型相对比较固定,关于异或的运算基本上都是线性基了:

  • 最大异或和
  • 第 kkk大异或和/异或和是第几大
  • 求所有异或值的和

线性基中的数记得开 long long
线性基的维数为 logslogslogs,sss 为数组中最大的值,用于 for 循环的上界。千万不能开大不然会WA

一个小技巧:

任意一条 111 到 nnn 的路径的异或和,都可以由任意一条 111 到 nnn 路径的异或和与图中的一些环的异或和来组合得到。

0x40. 最后的话

整理了两三天了,近三万字的全家桶,点个赞 (关注) 再走呗

部分内容参考李超2013年的WC课件,里面列举到的题目(Problem系列)有的是BZOJ上的题(BZOJ没了呜呜呜)有的应该是李超的原创题,找不到原题,有谁知道哪题的原题的话评论区里说一声呗 QwQ

参考资料:

  • 李超(北京大学)线性代数在OI中的应用与题目讲解
    (需要百度文库VIP,我充钱下载下来了,有需要找我)
  • OI中的线性代数
  • ACM中的线性代数基础
  • 高斯消元快速入门
  • 线性代数 —— 高斯消元法
  • oiwiki
  • 关于矩阵在ACM中的应用
  • 《线性代数》概念定理大全
  • 线性代数大全
  • 线性基学习笔记
  • 算法进阶课 y总yyds
  • 线性基学习笔记

【学习笔记】线性代数全家桶(在编程竞赛中的应用)相关推荐

  1. Linux学习笔记(3)- 网络编程以及范例程序

    Linux学习笔记(3)- 网络编程以及范例程序 前言 网络介绍 IP地址的介绍 端口和端口号的介绍 通信流程 socket介绍 TCP介绍 python3编码转换 TCP客户端程序开发流程 多任务版 ...

  2. 阿里云“7天实践训练营”入门班第二期学习笔记 第五天 在线编程挑战

    阿里云"7天实践训练营"入门班第二期学习笔记 第五天 在线编程挑战 吾辈,完全不会编程 以下内容全程来自阿里云社区的大佬分析讲解 原题目 知识点:搜索.字符串.位运算 有一天Jer ...

  3. c swap方法在哪个库里面_在编程竞赛中高效地编写C/C ++代码

    首先,您需要了解模板,宏和向量,然后再进行下一阶段! 模板是通用编程的基础,它涉及以独立于任何特定类型的方式编写代码. 宏是已命名的代码片段.每当使用该名称时,它就会被宏的内容替换. 向量与动态数组相 ...

  4. Android学习笔记(四):在Activity中跳转--Intent的使用

    Android学习笔记(四):在Activity中跳转--Intent的使用 上篇,我们实战了一个很小的项目BMI,通过BMI这个项目,可以很好的理解Activity的程序结构,以方便后面高级API的 ...

  5. Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题

    Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题 参考文章: (1)Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题 (2)http ...

  6. FreeRtos学习笔记(11)查找就绪任务中优先级最高任务原理刨析

    FreeRtos学习笔记(11)查找就绪任务中优先级最高任务原理刨析 怎么查找就绪任务中优先级最高的? tasks.c中声明了一个全局变量 uxTopReadyPriority,任务从其他状态进入就绪 ...

  7. C# 学习笔记(18)操作SQL Server 中

    C# 学习笔记(18)操作SQL Server 中 数据库基础操作 SQL语法可以参考 菜鸟教程 或者微软官方的SQL示例 注意SQL不区分大小写 查 1.基础查询 --最基础的查询语句, selec ...

  8. VSTO学习笔记(四)从SharePoint 2010中下载文件

    原文:VSTO学习笔记(四)从SharePoint 2010中下载文件 上一次我们开发了一个简单的64位COM加载项,虽然功能很简单,但是包括了开发一个64位COM加载项的大部分过程.本次我们来给CO ...

  9. 几何光学学习笔记(31)- 6.6 光学系统中光能损失的计算

    几何光学学习笔记(31)- 6.6 光学系统中光能损失的计算 6.6 光学系统中光能损失的计算 1. 透射面的反射损失 2. 镀金属层的反射面的吸收损失 3. 透射光学材料内部的吸收损失 4.总述 6 ...

  10. Slicer学习笔记(三十九)slicer中Markups模块

    Slicer学习笔记(三十九)slicer中Markups模块 1.概念 1.1.Markups模块简介 1.2.应用方向 1.3.界面面板 1.Markups List 2.Buttons And ...

最新文章

  1. 无向图的最小生成树(克鲁斯卡尔算法 Kruskal)
  2. 排除hotnews主题内容页面上的热点图片推荐里的分类-hotnews主题top_hot.php
  3. python面试常见问题-Python面试常见问题,涉及Python各个方面
  4. mysql怎么设置计划任务_mysql设置定时任务
  5. 春节添彩 福州花卉市场现“买花潮”
  6. 竟然被尤雨溪点赞了:我给Vue生态贡献代码的这一年
  7. 【北京】线下活动 | Azure SQL Database Managed Instance发布会
  8. Android2D绘图一
  9. javax.net.ssl.SSLException: closing inbound before receiving peer‘s close_notif---SpringCloud工作笔记111
  10. 作为IT人员,专业和不专业的差别有多大?
  11. 运动目标跟踪(十)--CSK跟踪
  12. 数独的优化回朔算法(二)
  13. 腾讯云启动 tomcat 很慢的问题
  14. 第九篇 设计模式之装饰模式
  15. 计算机组成模型机的视频教学,3CPU 3设计模型机 罗克露计算机组成原理课件(绝对与网上视频教程同步).pdf...
  16. 浙大计算机科学与技术专业课表,浙江大学 计算机科学与技术专业课程设置
  17. WeaveSocket框架-Unity太空大战游戏-服务端-1
  18. 职工科研项目管理系统的设计与实现附代码
  19. 我的Web安全学习之路
  20. Educational Codeforces Round 40千名记

热门文章

  1. Mask-RCNN论文解读
  2. Windows10 搭建java环境——JDK11的安装与eclipse的安装
  3. 除法取模(比赛常用)
  4. WSE 3.0 文档翻译:安装WSE3.0
  5. PO、VO、FormBean的思考
  6. idea导入gradle
  7. Fastlane实战(一):移动开发自动化之道
  8. 激活当前视图菜单高亮呈现 V2.0
  9. MySQL Replace INTO的使用
  10. Java实现文件分割和文件合并实例