基础线性代数(矩阵、高斯、线性基……)
(咕咕咕:矩阵的秩)
向量
定义就不用说了吧。。
定义向量空间 \(\mathbb{Z}^n\) 表示定义在整数域上的向量 \(\{a_1,a_2,\dots,a_n\}\),\(\mathbb{Z}^n_k\) 表示向量运算中逐位向 \(k\) 取模。
客串一个向量旋转的公式:如果要将一个二维实数域上的向量旋转 \(\alpha\),那么旋转后的向量为 \((x\cos\alpha-y\sin\alpha,x\sin\alpha+y\cos\alpha)\)。
定义线性空间 \(V\)。
矩阵
定义
一个 \(n\times m\) 的矩阵有 \(n\) 行 \(m\) 列,每一个交叉处有元素 \(A_{i,j}\)。
主对角线指 \(A_{i,i}\) 上的元素。
一般用 \(I\) 来表示单位矩阵,其中主对角线上为 \(1\),其他都是 \(0\)。
运算
矩阵加法:相同位置相加。
矩阵乘法:满足分配率、结合律,不满足交换律(矩阵与逆矩阵之间除外) 。
矩阵转置:记矩阵为 \(A\) ,则 \(A\) 的转置记为 \(A^T\) 。有基本性质:
矩阵求逆
对左半边的矩阵做高斯消元,同时更新右半边的部分,(交换时也一起交换,但最终不用再换回来了)。而做完之后的右半边部分就是求得的逆矩阵。
\(\bigstar\texttt{Trick}\):逆矩阵往往在给定方程组系数,但是反复修改每一个方程答案的时候有较快的加速作用。先对原矩阵求逆,那么逆矩阵就是组成这个元素答案每一个方程组答案的系数,\(\mathcal{O(n^2)}\) 解决。
逆矩阵在矩阵对角化中也有这举足轻重的地位和作用。
矩阵快速幂 - 常见模型
-
对于不含常数项的递推式:(比较正常的矩阵快速幂)
-
对于含有常数项的递推式:加上一维,在转移矩阵中不更改。
-
对于含有关于 \(n^1\) 的递推式:加上两维,每次后一位给前一位加一。
-
对于含有关于 \(n^k\) 的递推式:加上 \(k+1\) 维,例:
\(\bigstar\texttt{Attention!}\):由于转移矩阵与答案矩阵的大小不同,应该在 struct
的矩阵中记录这个矩阵的大小,防止将 \(O(n^2)\) 变为 \(O(n^3)\) !!!(如[NOI2020] 美食家中就需要预处理 \(\log v\) 次矩阵)
高斯消元
高斯消元法除了用于线性方程组求解外,还可以用于行列式计算、求矩阵的逆。
如果将每一个方程的系数忽略,将矩阵的每一列理解为空间向量,发现在消元过程中我们做的就是求出这组向量的基底,可以用这些表示出其他所有向量(即 \(k\) 进制或实数域下的线性基?)
My Code
scanf("%d",&n);
for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++) scanf("%lf",&a[i][j]);
for(int i=1,Max=1;i<=n;Max=++i)
{
for(int s=i+1;s<=n;s++) if(fabs(a[s][i])>fabs(a[Max][i])) Max=s; // 找出绝对值最大的
for(int j=1;j<=n+1;j++) swap(a[i][j],a[Max][j]);
if(a[i][i]<10e-8 && a[i][i]>-10e-8) { p=false; break; } // 记得 double 的精度问题
for(int s=1;s<=n;s++) if(s!=i) // 这样省去了第二步处理的麻烦
{
double tmp=0-(a[s][i]/a[i][i]);
a[s][i]=0;
for(int j=i+1;j<=n+1;j++) a[s][j]+=tmp*a[i][j];
}
}
if(p) for(int i=1;i<=n;i++) printf("%.2lf\n",a[i][n+1]/a[i][i]);
else printf("No Solution\n");
线性基
大致内容与性质
线性基为一个数集构造出来的新数集,满足以下性质:
- 线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值。
- 线性基是满足性质 \(1\) 的最小的集合。
- 线性基没有异或和为 \(0\) 的子集。
- 线性基中不同的异或组合异或出的数都是不一样的。
- 线性基中每个元素的二进制最高位互不相同。
基本功能
-
查询一个数是否可以被异或出来。
- 直接从高位到低位异或过去,如果最终等于 \(0\) 的成功。
- 如果需要求出由那些数异或出来(只会由不超过 \(\log\) 个数异或出),在每一位记录由那些位异或出来,每次查询、加入的时候一样异或过去即可。
-
查询异或最小、最大值。
-
查询可以异或出来的第 \(k\) 大值。
- 将线性基中元素重构为互相没有相同的二进制位都为 \(1\)。
-
查询一个数在所有可以被异或出来的数中的排名。
-
查询异或出 \(k\) 的方案数:
- 结论:如果可以异或出 \(k\),那么方案数就是 \(2^{n-size}\),其中 \(size\) 是线性基中的元素个数。
- 证明:线性基中元素互不影响,而其他 \(n-size\) 个元素公有 \(2^{n-size}\) 种异或方案。如果 \(k\) 能够被表示,那么对于任何一种方案,在线性基中都有且仅有一种对应方式使得异或和为 \(k\),证毕。
基础模板:
void Insert(ll x) // 加入一个数
{
for(int i=62;i>=0;i--) if((x>>i) & 1)
{
if(!p[i]) { p[i]=x; break; }
x^=p[i]; // 更新 [0,i-1] 位的更优答案
}
}
bool exist(ll x) // 查询一个元素是否可以被异或出来
{
for(int i=62;i>=0;i--) if((x>>i) & 1) x^=p[i];
return x==0;
}
ll query_max() // 查询异或最大值
{
ll ret=0;
for(int i=62;i>=0;i--) if((ans^p[i])>ans) ans^=p[i];
return ans;
}
ll query_min() // 查询异或最小值
{
for(int i=0;i<=62;i++) if(p[i]) return p[i];
return 0;
}
ll kth(ll k) // 查询异或第 k 小
{
// 重建 d 数组,求出哪些位可以被异或为 1
// d[i] 中任意两个数在任意一个二进制位上不可能同时为 1
for(int i=62;i>=1;i--) // 从高到低防止后效性
for(int j=i-1;j>=0;j--)
if((p[i]>>j) & 1) p[i]^=p[j];
for(int i=0;i<=62;i++) if(p[i]) d[cnt++]=p[i];
if(!k) return 0ll; // 特判 0
if(k>=(1ll<<(ll)cnt)) return -1ll; // k 大于可以表示出的数的个数
ll ret=0;
for(int i=62;i>=0;i--) if((k>>i) & 1) ret^=d[i];
return ret;
}
ll Rank(ll k)
{
ll Now=1,ret=0;
for(int i=0;i<=62;i++) if(p[i]) // 记得保证 k 可以被异或出来
{
if((k>>i) & 1) ret+=Now;
Now<<=1;
}
return ret;
}
应用
O(logn) 求区间异或最大值
题意:给定一棵树,求 \(x\) 到 \(y\) 路径的异或最大值。
用 \(p[x][]\) 表示点 \(x\) 到根之间所有点的线性基,同时维护 \(pos[x][]\) 表示这一线性基由哪一个点转移而来。
在 \(\text{Dfs}\) 加入一个新的点时,贪心将贡献相同或更高,且深度更大的点代替原来线性基中的值。
这样查询的时候就能在 \(O(\log_{2} n)\) 复杂度内求出在点 \(x\) 到 \(Lca\) 路径中的点的最大异或和(更深的点已经加入线性基)。
题意:求出序列中 \(l\) 到 \(r\) 的区间最大异或和。
和上一题差不多,将维护更深的点转化为维护更靠后的点即可。
线性基推广 - 非二进制
P3265 [JLOI2015]装备购买:将线性基推广到实数域上。
CF832E Vasya and Shifts:\(5\) 进制线性基(用高斯消元的方法实现)
在没有特殊性质的情况下,我们都用类似高斯消元的思想完成求出向量基底的操作,插入单个元素 \(\mathcal{O(n^2)}\),整体 \(\mathcal{O(n^3)}\)(PS:你可千万别真的去做矩阵高斯消元了哈,是指在插入线性基的时候,更新 \([0,i]\) 位的答案时用的)
\(\texttt{update}\):如果我们需要快速实现三进制的“高斯消元”(或者说循环卷积),可以对这一位为 \(1,2\) 分别记录这些位置的集合,用二进制数表示,这样快速实现对不同位的操作。(存这个三进制数的时候要存下这两个二进制数)
P4151 [WC2011]最大XOR和路径
由于可以走到环上去,让我们的问题变得比较复杂。
发现一中合法行走的情况一定是一条路径加上一堆环。
那么只用将所有简单环处理出来丢到线性基中,在将每一条路径丢到线性基中就好了。
发现每一个环其实都是一棵生成树的两条根到点的路径加上一条横插边,路径就是两条根到点的路径的异或和。
所以直接将每个根到点的路径和环丢到线性基中求异或最大值即可。
CF724G Xor-matic Number of the Graph
和上一题都是将根到每个点的路径求出来,但是需要求路径和环异或起来可能产生的值的和。
先考虑一个弱化版:给定 \(n\) 个数,求他们中选出若干个数异或起来可能产生的值的和。
拆开每一位,考虑第 \(w\) 个二进制位:(\(size\) 为线性基中元素个数)
- \(n\) 个元素的线性基中,有元素在 \(w\) 上为 \(1\)。那么有 \(2^{size-1}\) 中方案使得这一位为 \(1\),这一位的贡献为 \(2^{size-1}2^w\)。
- 如果这一位为 \(0\),贡献为 \(0\)。
回到这个问题,首先处理出所有环的线性基,考虑在第 \(w\) 个二进制位上的贡献。
-
如果线性基中有元素在 \(w\) 上为 \(1\),则路径端点 \(u,v\) 可以随便选择,贡献为 \(2^{size-1}2^w\binom{n}{2}\)。
-
如果线性基中所有 \(w\) 位上都为 \(0\),则要求根到 \(u,v\) 有且仅有 \(1\) 个为 \(1\),记根到点的路径上有 \(x\) 在这一位为 \(1\),则贡献为 \(x(n-x)2^{size}2^w\)。
这样将所有贡献加起来即可,记得整张图不一定连通,对每个连通块分别计算即可。