线性基学习笔记
是什么
常用于处理线性无关或最大异或和或最小异或表示集(即最小能用多少个数的异或和表示出原集合中的数)等问题
线性基,是一个向量集合,是一种压缩向量集合的手段,因此可以降低复杂度。
具体而言,就是线性基里的若干向量通过线性操作表示出原集合的每一个元素,且线性基内部的向量全部线性无关
因此线性基内的向量个数仅为向量的维度n。
对于这个,我们从方程的角度去理解:
把每个向量看作一条方程,向量的每一维对于一个未知数
即m个n维向量,相当于m条n元方程
如果一条方程能被其他方程通过线性运算表示出来,那么它就可以被去掉。
最后的结果就是该集合的线性基
怎么做
构造线性基的过程可以类比高斯消元。
我们最终会把线性基中的向量集化为上三角式矩阵,如下图。
具体而言,每加入一个向量,我们就从上到下将该向量依次减去每一条向量,每减一次就将一个变量消掉,直到无法再消为止
这样就可以得到一个上三角矩阵了。
代码如下
vector<data> vec; int n,m; bool insert(data x) { int p1=1,p2=1; while(fabs(x.arr[p1])<eps&&p1<=m) p1++; for(int i=0;i<vec.size();i++) { while(fabs(vec[i].arr[p2])<eps&&p2<=m) p2++; if(p1>p2) continue; if(p1<p2) break; double t=x.arr[p1]/vec[i].arr[p1]; for(int j=p1;j<=m;j++) { x.arr[j]-=vec[i].arr[j]*t; } while(fabs(x.arr[p1])<eps&&p1<=m) p1++; } if(p1<=m) { vec.push_back(x); for(int j=vec.size()-1;j;j--) { int t=1; while(fabs(vec[j-1].arr[t])<eps) t++; if(p1<t) swap(vec[j],vec[j-1]); else break; } return true; } return false; }
扩展
处理异或和的线性基是线性基中的一种
就是把每个数看作一个向量,它的每一个二进制位看作一维,这样就把每个数变成了一个31维向量,注意构造过程中要特殊化处理。
代码如下
struct linerbase { int val[32]; //最小异或表示集即为val中不为0的项 void insert(int x) { for(int i=31;i>=0;i--) { if(((x>>i)&1)==0) continue; if(!val[i]) { val[i]=x; return; } x^=val[i]; } } linerbase() { memset(val,0,sizeof(val)); } //查询最大异或和 int query(int x) { for(int i=31;i>=0;i--) if((x^val[i])>x) x^=val[i]; return x; } };
看都看了,顺手点个推荐呗 :)