线性基 学习笔记
1. 线性空间的定义
线性空间(linear space. a.k.a. 向量空间,vector space)是一个关于向量加法和标量乘法的封闭向量集合。
注意:这里的向量加法和标量乘法可以是广义的,如 OI 中广泛地将向量加法定义为异或。但它们需要满足交换律、结合律、分配律。
e.g.:
给定向量
e.g.:若有
若对于线性空间中的一些向量
则称它们线性相关(linearly dependent),否则它们线性无关(linearly independent)。(此处定义和蓝书不同,下文将说明两种定义是一致的)
线性无关的生成子集被称为线性空间的基底(basis),简称基(a.k.a. 线性基)。 基的大小成为线性空间的维数(dimension)。
e.g.:
对一个
2. 线性空间的基本性质
- 性质:线性空间中存在零元(一个)、单位元(一个)、负元和逆元。
证明:由封闭性易证。(简言之,线性空间存在两种运算、四个元)
- 性质:过原点的直线、平面(或者别的任意维空间)构成一个线性空间。
证明:由空间的方程和线性空间的定义显然。
- 性质:对确定的线性空间任两个基的大小相等,均为维数。
证明:考虑两组基
可得
由于
综上,确定的线性空间维数唯一。
- 性质:
n+1 个n 维向量总是线性相关。
证明:类似高斯消元法易知。
- 性质:向量
\{\bold a_1,\bold a_2,\cdots,\bold a_n\} 线性相关的充要条件为其中的一个向量能被其余向量表出。
证明:必要性:把定义式抄过来移项,将其中一个的系数除过去就能构造一组表示。充分性:把表达式移项,显然成立。
- 性质:若向量集合
\{\bold a_1,\bold a_2,\cdots,\bold a_n\} 的一个部分组线性相关,则总体线性相关;若集合总体线性无关,则它的任意部分组线性无关。
(也称“部分相关,总体相关;总体无关,部分无关”)
证明:由上一条性质易证。
- 性质:行秩 = 列秩。
证明:将行空间进行行变换得到
可得
- 性质:
r(AB)\le \min\{r(A),r(B)\}
证明:由于
同理
3. OI 中的线性基
在 OI 中,我们把每个数根据其二进制写为一个向量,以这些数为生成子集张成一个线性空间。
有线性基的定义及性质,可以轻易地导出以下结论:(摘自 OI-Wiki)
- 线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值。(定义 - 生成子集)
- 线性基是满足性质 1 的最小的集合。(性质 - 维数唯一)
- 线性基没有异或和为 0 的子集。(定义 - 线性无关)
- 线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。(定义 - 线性无关)
OI 中的线性基一般用于解决异或相关问题。
那么,什么时候用 01Trie 呢?一般来说,确定个数(一般为两个)的异或值考虑 01Trie,不确定个数的异或值考虑线性基。
4. 线性基的计算
区别于上文讲的二进制意义下的线性基,这节里指的都是一般的线性基。(当然二进制意义下的线性基也可这么计算)
这样的线性基可以采用高斯消元的方式计算,我们一般采用行空间进行求解。
具体地,对于每一维,我们寻找当前此维值最大的一行,并指定其为线性基的一部分,进行消元:
fo(i,1,m) {
int cur=i;
fo(j,i+1,n)
if(fabs(a[cur][i])<fabs(a[j][i]))
cur=j;
fo(j,1,m) swap(a[i][j],a[cur][j]);
if(fabs(a[i][i])<=eps) continue;
fo(j,1,n) if(j!=i) {
db v=a[j][i]/a[i][i];
fo(k,1,m) a[j][k]-=a[i][k]*v;
}
}
消完元我们再遍历一遍所有的行,如果此行内所有的值都是
int cnt=0;
fo(i,1,n) {
bool flag=false;
fo(j,1,m) if(fabs(a[i][j])>eps) {
flag=true; break;
}
if(flag) cnt++;
}
上面代码中的cnt就是矩阵的秩。换言之,我们可以用高斯消元在
如果要用高斯消元法求二进制意义下的线性基,消元的那个部分等价于整数的异或,这意味着它的复杂度此时是
5. 线性基的维护
对线性基的计算还有一种方式,就是采用每次动态地把数加入线性基的方式。
在 OI 中,我们一般默认线性基中任意两个元素的二进制最高位不同。若不然,可以将它们异或得到一个新数取代掉其中一个,这是等效的。
因此,我们可以对每个二进制位记录一个
要插入一个数
-
如果
x 在此位上为0 ,那么它不能在这一位上产生贡献,直接忽略。 -
如果
x 在此位上为1 ,那么我们尝试将x 插入:-
假如此时
cur_i=0 ,直接将它赋值为x 。 -
假如此时
cur_i\ne 0 ,根据上文提到的解决方法,我们它们异或得到一个新数取代掉其中一个。这里取代掉
x ,继续循环寻找能插入的位。
-
void insert(ll x) {
fr(i,55,0) {
if((x&(1LL<<i))==0)
continue;
if(cur[i]==0) {
cur[i]=x; break;
}
x^=cur[i];
}
}
如果要查询一个数
如果当前位为 false。
否则就用
若程序运行到尾部,说明此时 true。
bool query(ll x) {
fr(i,55,0) {
if((x&(1LL<<i))==0)
continue;
if(cur[i]==0) return false;
x^=cur[i];
}
return true;
}
总复杂度
6. 例题
- P3812 【模板】线性基(线性基模板)
板子题,直接把插入式线性基放上。那么怎么求最大值呢?
我们还是贪心地从大到小尝试异或上
fr(i,55,0) ans=max(ans,ans^cur[i]);
Code
- P3265 [JLOI2015]装备购买(高斯消元式线性基模板)
也是板子,把高斯消元式线性基放上。
还有就是要注意精度
Code
- #114. k 大异或和(经典题)
这题是经典题,有两种做法。
-
蓝书上采用了高斯消元的方法。先一遍消元求出秩与线性基,然后贪心地加入答案。
不过复杂度高达
\Theta(n^2\log a_i) ,这题显然超时。 -
在网上搜到的做法,采用插入式线性基。
那么怎么保证
这样我们采用类似 BST 找 k 大时的操作即可。(不过要注意这里的第
fr(i,55,0) if(cur[i]>0)
{
if(res<(res^cur[i])) {
if(k>=(1LL<<pos[i]))
k-=(1LL<<pos[i]), res^=cur[i];
} else {
if(k>=(1LL<<pos[i])) k-=(1LL<<pos[i]);
else res^=cur[i];
}
}
最后要注意特判一个
Code
- P4570 [BJWC2011]元素(线性基的简单应用)
和板子也没差多少,高斯消元求一个线性基即可。
千万要注意消元时循环起始点的位置不一定是
fo(i,0,62) {
int cur=-1;
fo(j,cnt+1,n) if(a[j]&(1LL<<i))
if(cur==-1||b[j]>b[cur])
cur=j;
if(cur==-1) continue;
cnt++;
swap(a[cnt],a[cur]), swap(b[cnt],b[cur]);
ans+=b[cnt];
fo(j,1,n) if(j!=cnt) if(a[j]&(1LL<<i))
a[j]^=a[cnt];
}
Code
- P4301 [CQOI2013] 新Nim游戏(线性基与博弈论)
回顾一下 nim 游戏取胜的条件:后手必胜当且仅当当前所有堆中的个数的异或和为
那么在这道题中,我们应当尽可能地让后手在第二回合取完以后异或和不为
那么先手怎么取才能让后手在第二回合取完以后异或和一定不为
这就说明以第一回合后的集合是线性无关的,即它是一个线性基。
这样就变成了一个线性基板子题。一般来说,线性基在博弈论中的出现往往是 nim 游戏引出的。
Code
- P4151 [WC2011]最大XOR和路径(线性基与图论)
显然这题如果用 01trie 做空间、时间都会爆炸,可以用线性基来减小规模。
我刚开始的一个想法是像最短路那样去跑,但是这样可能会把两条从
正确的做法应该是很神仙的,是通过扩展仙人掌上的做法得到正解。
具体地,我们找出一条从
那么怎么找环呢?我们 dfs 整棵树,对所有的返祖边、横叉边暴力统计即可。
但是,这张图不是仙人掌,为什么这样是对的呢?实际上,由于异或的性质,我们可以通过异或得到所有的简单环。
有一个注意点是从
时间复杂度
Code
事实上,如果求的不是最大值,而是对点对
见CF724G Xor-matic Number of the Graph。
- P5607 [Ynoi2013] 无力回天 NOI2017(线性基与数据结构 / 维护区间线性基)
线性基显然没有办法满足区间可减性,但是可以实现快速合并,因此可以直接用线段树搞。
不过这个区间xor操作有点难搞,Lazy-Tag 也难传。基于这东西是区间可减的,我们可以大力差分。
令
异或意义下,
这样就好了(我可能做了个假的Ynoi)。时间复杂度
Code(一发AC啊啊啊啊啊啊啊!!!)
- CF1163E Magical Permutation(线性基的灵活应用)
是一道 CF 风格的构造题,也是黑题 掉紫了(大悲),非常神仙。(推完必要性发现不会做,看了题解awa)
我们先尝试求这个
首先,其实相邻两数的异或值的本质就是差分,倒着转化为前缀和,那么相当于每个数就是由这些数其中的一些异或而成的。
这说明,每个数都在由这
hack:
2
4 5
按照上面的这个算法,
问题正出在数的大小上:容易知道,如果这两个数是 0 1,那么它就合法了。我们发现,对于一个
那么我们把这
那么如何构造呢?根据线性基的性质,
那么我们可以类似状压,用
由于前缀的性质,答案排列中相邻两位所对应的二进制至多相差一位。
这其实就是格雷码!也就是说,我们直接生成几个格雷码就行了。
最后要注意,这里线性基中的数不是
Code
完结撒花~~