线性基佐料

发布时间 2023-12-31 16:15:40作者: 2008verser

cnblogs 中阅读。

【少图预警!】【需要结合其他文章食用!】

?声明?

这里不对线性代数相关概念和异或线性基做最基本的概述。

上网搜大概可以搜到三篇高质的讲解线性基的博客:

线性基小记 - command_block 的博客 - 洛谷博客 (luogu.com.cn)

线性基学习笔记 - 拜傅里叶教总部 - 洛谷博客 (luogu.com.cn)

线性基学习笔记 | Menci's OI Blog

下面两篇适合入门。上面那个没点线性代数基础感觉看不太懂。

最下面这一篇介绍的线性基构建方法比较麻烦。

它保证了每一个存在于线性基的位置 \(i\),仅有 \(a_i\) 的第 \(i\) 位为 1。

但这个性质完全可以在构建完线性基后,扫一遍来完成。将在模板习题给出。


线性基的性质,除了常说的,基最小、基线性无关、基的张成等于原集合的张成、原集合元素的唯一表示以外,还有很多,我们将在例题逐一分析。

读者注意留意题目中各种映射关系,有助于理解。

为防不知道有没有的读者和我用的循环压缩不一致,下面的代码默认有这两行 #define

#define rp(i,r,l) for(int i=(r);i>=(l);--i)
#define fo(i,l,r) for(int i=(l);i<=(r);++i)

全文 w 定义为 \(\log V\)


模板线性基:P3812 【模板】线性基

这里补充一个构造线性基时需要用到的结论,或有助理解:

  • 将基中的任意一个元素异或上基中的另外一个元素,基仍是基。

证明考虑,设是 \(x\) 异或上了 \(y\) 变成 \(x'=x\oplus y\),把原张成的每个含 \(x\) 的元素,替换成 \(x'\oplus y\) 即可。

根据「基的张成等于原集合的张成」,我们可以对输入的全部数求基。

那么问题变成了,我们在基里面选若干元素使异或和最大。

到这里,可以使用前两篇博客中的构造方法,写出这样的代码:

void insert(ll x) {
	rp(i,w,0) {
		if(x&(1ll<<i)) {
			if(!a[i]) { a[i]=x;return; }
			x^=a[i];
		}
	}
}
ll query() {
	ll ret=0;
	rp(i,w,0) if((ret^a[i])>ret) ret^=a[i];
	return ret;
}

但是这样丝毫不能体现我们对线性基的高超技艺。

我们可以使用第三篇博客的构造方法,这样构造出的基,如果第 \(i\) 位存在,那么第 \(i\) 列仅这一个为 \(1\)。(可以去 Menci 博客里好好看看,这里不详细讲)

也就意味着一个非零的行,我们选他,一定更优。

那么全部异或起来就对了。

事实上,Menci 博客里面的构造方式有些繁琐。我们可以按照前两篇博客的构造方式先构造一组线性基。

因为线性基里的数随便异或不会改变任何事情。我们可以不断异或,使得这组线性基具有 Menci 博客中提到的性质。如下:

void insert(ll x) {
	rp(i,w,0) {
		if(x&(1ll<<i)) {
			if(!a[i]) {
				a[i]=x;
				return;
			}
			x^=a[i];
		}
	}
}
void prepare() {
	fo(i,0,w) {
		rp(j,i-1,0) {
			if(a[i]&(1ll<<j)) a[i]^=a[j];
		}
	}
}
ll query() {
	prepare();
	ll ret=0;
	rp(i,w,0) ret^=a[i];
	return ret;
}

第二种方法,我们的 prepare() 操作就是 command_block 博客里提到的:“将三角基进一步消成对角基”。(虽然我也不知道这俩是啥。)

在执行完 prepare() 以后,我们的基就有 Menci 博客里介绍的全部性质了。

模板题到这里就结束了。时间复杂度,三角基是 \(O(n\log V)\)。消成对角基要多一个 \(\log V\)

Code for this.


P3857 [TJOI2008] 彩灯

即,计算给定集合的张成与 \(\{0\}\) 的并的大小。

直接对原集合求张成大小是不会的。所以我们先求基。

根据「原集合元素的唯一表示」以及「基线性无关」,我们知道基的每个元素选,或不选,组合成的数于原数组都是不重不漏的。

那么一共能组合出 \(2^k\) 次方个数。\(k\) 是基的大小。

至于零,输出时加一就好了。

时间 \(O(mn)\)

Code for this.


P4301 [CQOI2013] 新Nim游戏

首先我们要熟知经典 Nim 游戏的结论:当一个局面全部石子个数的异或和为零,必败。否则必胜。

在这道题中,第一回合我们需要拿走若干堆石子,使得无论对手取哪几堆石子,都会送我们一个必胜局面。

也就是说,使得对手不能将局面变为,全部石子个数异或为零。

也就是说,我们拿完第一次,剩下的石子不存在一个非空的子集(因为对手可以不拿,所以不是真子集),使得异或和为零

也就是说,剩下的石子线性无关

注意基的定义,基没有异或和为零的子集。

且,「基,是原集合全部线性无关子集中,集合大小最大的」。

因为我们要最小化第一回合我们取的石子数量,所以剩下的石子要大。那么我们留一个基就好了。

为了最大化基,我们降序排序,顺序枚举,能选就选。

如果向 \(a_{1\to i-1}\) 形成的基中加入 \(a_i\),变得线性相关,说明 \(a_i\) 可以被 \(a_{1\to i-1}\) 一些表示出来。

此时我们要么保留原本的基,要么从原本的基删掉一个元素,加入 \(a_i\)

注意到,这两种基都是由 \(a_{1\to i-1}\) 形成的基,张成一样。即完全等价。

那么,对于两种等价的基,我们保留原本的基,答案不会更劣。

所以这是对的。

这种,「选取线性无关子集,最大化求和」或是「留下线性无关子集,最小化选取求和」的问题,以后还会遇到很多次,都可以用这种方式解决。

时间 \(O(k\log V)\)

Code for this.


P4570 [BJWC2011] 元素

做过【P4301】这道题就算裸了。请读者自行完成。


P3292 [SCOI2016] 幸运数字

若我们得到两点间的线性基,事情就很典了。

注意线性基大小是 \(\log V\) 级别的。那么我们可以通过把一个线性基里的数加入另一个完成时间为 \(O(\log^2V)\) 的合并。

那我们有两种方法:

  • 获得 \((x,lca)\)\((y,lca)\) 的线性基,合并。
  • 树剖+线段树或者倍增维护线性基。

询问是不确定的,所以第一种方法不行。(可以使用前缀线性基 \(O(q\log^2V)\),但这里先不提前介绍)

从线段树询问区间线性基,会拆成 \(\log n\) 个区间,合并这些要 \(\log^2V\times\log n\)

树剖需要 \(\log n\) 次线段树询问。所以时间是 \(O(q\log^2V\log^2n)\) 的。

注意线性基是可重复贡献的。可以用 ST 表维护某个点向上 \(2^i\) 的线性基。\(O(n\log^2V\log n+q\log ^2V)\)

\(n\)\(q\) 小一些,第二个做法较优。

Code for this.(这里是树剖+线段树,ST 表的代码可以去题解区找)


P4151 [WC2011] 最大XOR和路径

这次变成图了。没环的话跟上一题一样。

如果从一个点开始,走到一个环,绕一圈,原路回来,此时获得的贡献仅是环的。意思是每个环都可以随便选

记由「若干返祖边和返祖边跨过的边形成的环」为特殊环

建出 dfs 树。那么每个环都可以由若干特殊环通过异或组成。如下图,再归纳即可。

那么我们此时要选一条从 \(1\)\(n\) 的最优的路径,最优是指它选一些环异或起来最大。

当我们知道哪一条最优以后,问题就变成了已知的路径异或和(第一步),要任选一些 \(a_i\) 异或异或起来最大(第二步)。\(a_i\) 就是各个环的异或和。

既然每个环都在特殊环的张成里,我们对特殊环建线性基。那问题就是第一步。

总不能枚举全部路径。观察两条从 \(1\)\(n\) 的路径。它们构成了一个环。

那么,如果我选了一条 \(1\to n\) 的路径 \(A\),然而 \(1\to n\) 的最优路径是一条不等于 \(A\) 的路径 \(B\)

那么在我选一些环使得异或和最大的时候,我会选出 \(1\to n\to 1\) 这一个环,且这个环由 \(A,B\) 构成。

此时我的第一部分异或就变成了 \(B\)

所以第二步会令第一步最优。第一步随便找就好了。

时间 \(O(n+(m-n)\log V)\)

Code for this.


前缀线性基:CF1100F Ivan and Burgers

提醒:a[i] 指基元素,\(a_i\) 指序列。

这,就是上上题提到的,前缀线性基!

我们对于每个 \(i\) 维护一个 \(a_{1\to i}\) 形成的线性基。

这个基,由尽可能靠后的数组成。

什么意思?

我们要理解两件事:

  • 基不唯一,且可以是原集合的子集。

(如果你是线代大师会觉得这很显然)

举个例子,\(a=\{1,2,3\}\),一个合法的基是 \(\{2,3\}\)

\[\begin{aligned} 10\\ 11\\ \end{aligned} \]

但是这种基并不会被我们构造出来,因为他们的最高位都是 \(2^1\) 位。

我们构造的应是:

\[\begin{aligned} 01\\ 10\\ \end{aligned} \]

这两种基本质是相同的。我们可以对第一个基进行内部异或运算。它一定可以变成第二个。(全部基都可以这样,证明考虑【模板】的证明)

  • 常见的线性基,由尽可能靠前的元素组成。

因为我们是贪心构造:如果能加就加了。

  • 综上,「构造线性基」得到的结果(后面两句不是流程!只是结果一样!),是选最靠前的,再进行基内部异或使得不存在两个最高位相同的元素。

内部异或不会异或出零,因为我们选了

这两条性质很重要。影响到后面题目的完成。

那么,选尽可能靠后的数构成线性基,即从后往前做 insert()

那对于每个位置 \(i\) 处理出 \(a_{1\to i}\) 尽可能靠后的元素形成的线性基,下意识记录基元素 a[i] 在原数组下标,记 p[i]

询问时不要算 p[i]<la[i] 就好了。

我们发现它 TLE 了(而非 WA )。所以分析是对的。

考虑利用好 \(i-1\) 的信息,令 \(i\) 的线性基从 \(i-1\) 复制过来。接着想办法把 \(a_i\) 正确加入

使 \(a_i\) 必须加入。

  • 若加入 \(a_i\) 基仍线性无关,则代码会自己顺序执行并插入 \(a_i\)
  • 否则会冲突。

有一种很自然的解决方式,如代码:

void insert(int x,int id) {
	rp(i,w,0) {
		if(!x) return;
		if(x&(1<<i)) {
			if(!a[i]) { a[i]=x;p[i]=id;return; }
			if(id>p[i]) swap(p[i],id),swap(a[i],x);
			x^=a[i];
		}
	}
}

我们遍历过的 a[i] 已经是尽可能靠后,并且当前 a[i] 尽可能靠后,并且最终基的张成不变,归纳可证新基由尽可能靠后的元素形成。(信息量很大)

请你联系引用框段,好好思考。

你会明白此时的基,a[i],是原序列第 p[i] 位经基内异或得到的结果。

虽然对这题无意义。

那么我们成功得到了第 \(i\) 位的基。

时间 \(O(n\log V+q\log V)\)

Code for this.


CF845G Shortest Path Problem?

与 WC2011 一样。请读者自己思考。


实数线性基:P3265 [JLOI2015] 装备购买

事情变得稍稍有点意思了。

这次,线性组合不再是常见的 Xor 运算,而是线代中的「线性组合」,也即,向量的缩放和加法

\(n\) 件武器视作 \(n\)\(m\)向量

如果我们能维护这样子的「线性组合」意义下的「线性基」,这道题就变得很经典了——排序。

照葫芦画瓢,我们重新思考异或线性基中各个部分的意义。

  • 基、张成,很显然。
  • 内部异或,张成不变。

内部异或→内部线性组合→矩阵的行初等变换。

即,矩阵做行初等变换,张成不变。证明与先前类似。

  • insert() 时,\(i\)w0 枚举,就赋值,非空就异或。

空,指没有最高位为 \(2^i\) 的基元素。

异或,指消掉待加入数的 \(2^i\) 位。

变为:从最高维开始,向最低维枚举,若基中没有最高维是第 \(i\) 维的变量,赋值;否则将待加入的向量第 \(i\) 维消为零

P.S. 为了和异或线性基一致,定义属性从 \(1\)\(m\) 为从高到低。

那就会做了。

可以联系这篇博客的图。

为了实现常数,我们不真的像原本那样建立新数组存基,转而记录最高维是第 \(i\) 维的变量在原数组的下标。并在原数组进行消元。

时间 \(O(nm^2)\)

同时我们发现,这才是线性基真正的复杂度。

平时的异或线性基为什么少了个 \(m\) 呢?因为消元运算被异或运算优化成 \(O(1)\) 的了。

Tangninghaha 做过一道可删线性基《八纵八横》。这里维度很高,可以用 bitset 优化。\(\frac{m^2}{\omega}\)

你也可以理解为平时 \(V\)long long 以内的是除了个 \(\omega\)

这道题精度开 1e-5 即可。不然会 WA。

Code for this.


P5556 圣剑护符

询问两点间点权是否线性相关。

基元素最高位不得相同。因此,基的元素不超过 \(\log V\)w )个。

当原集合大小大于 w 时,必有元素无法插入,即线性相关

所以选择性暴力回答询问或输出 YES

修改我选择了利用 dfn 序差分。或许还有别的做法。

这是 \(O(n\log V+q\log^2 V)\)

Code for this.


CF724G Xor-matic Number of the Graph

像 WC2011 那题一样构造特殊环的线性基 \(circle\)。先表示出来答案。

就是对于全部的 \(u,v\),对 \(\sum_{x\in\text{span}(circle)} dis(u,v)\oplus x\) 求和。

任意建生成树,设 \(d_x\)\(x\) 到根的异或和。\(dis(u,v)\) 就变成了 \(d_u\oplus d_v\)

枚举 \(u\),此时我们有三组数,第一组是 \(d_u\) 一个,第二组是 \(v\) 大于 \(u\)\(d_v\)(为了不算重),第三组是特殊环的基。

我们要做的就是每组任选一个,Xor 起来,求和。

这里我们可以按位贡献。统计下面两组每个位置 0/1 数量。

用简单的组合数学做。就不细讲了。

注意图不一定联通。

时间 \(O(n\log V)\)

Code for this.(我当时写的时候每个 \((u,v)\) 算了两次,最后除以 \(2\),懒得改了。)


CF895C Square Subsets

注意 \(70\) 以内质数共有 \(19\) 个。

某个数是完全平方数,当且仅当,它每个素因子的次数为偶数。

我们可以把每个 \(a_i\) 变成一个长 \(19\) 的二进制串,代表 \(a_i\) 中每个素因子次数的奇偶性。

非空子集乘积为完全平方数,当且仅当,这些二进制串 Xor 为零。

解法一

那么我们状压。\(f_{i,j}\)\(i\) 个二进制串,Xor 等于 \(j\) 的非空子集数量。转移很显然。

怎么优化?注意 \(1\leq a_i\leq 70\),所以本质不同的二进制串至多 \(70\) 个。

可以把若干个相同的压起来,dp 时乘一乘即可。

时间 \(O(2^{19}V)\)。(不要跟我争是 \(O(V)\)。)

Code for this,312ms.

解法二

线性基!「非空子集异或和为零」是「非空子集线性相关」充要条件。

求有多少非空子集线性相关。延续前面几题的思想,一个基对应着若干个原序列的元素。(你忘了的话请看「前缀线性基」的引用段落)

剩下的元素的任意非空子集都有一个 Xor 值,且我们可以唯一选取选基的一个子集(可为空)使得它的异或和异或上 Xor 等于零。

哦!

我们每选取剩下元素的一种非空子集,对应着一种不同的选取方案(基里唯一选取)。

且,每一种合法选取,必对应上面这里的一种选取。

他们是双射!

答案就是 \(2^{n-l}-1\)!(\(l\) 是基大小)

减一你猜。

时间 \(O(19n)\)

Code for this,46ms.


CF1163E Magical Permutation

我们可以先枚举 \(x\),判断是否存在相邻两个数的 Xor 属于 \(S\) 的、\(0\to 2^x-1\) 的排列。简记 \(n=2^x-1\)

设排列的第一个数为 \(\alpha\),第二个为 \(\beta\)。设排列相邻两两异或值为 \(S_{i_1\to i_{n-1}}\)

\(\alpha\oplus\beta=S_{i_1}\),即 \(\beta=\alpha\oplus S_{i_1}\)

那可以表示出整个排列:

\[\begin{aligned} &\alpha\\ &\alpha\oplus s_{i_1}\\ &\alpha\oplus s_{i_1}\oplus s_{i_2}\\ &...... \end{aligned} \]

\(\alpha\) 不知道哎!没关系!把排列每个数异或上 \(\alpha\),得到的还是一个排列。

\[\begin{aligned} &0\\ &s_{i_1}\\ &s_{i_1}\oplus s_{i_2}\\ &...... \end{aligned} \]

现在要解决:不断从 \(S\) 里选数,和上一个数异或,得出新一个数。重复这个操作,要构造排列。

我们设想一下,如果新选的数没被选过,那构造出的一定是新数。

否则,新选的数会和原先选过的异或相消,相当于删了一个数。

所以,这其实是不断在集合中加数、减数的一个过程。

我们用到的是全部最高位不超过 \(2^x\)\(S\)。这些元素的张成 \([0,2^x-1]\),是有解的充要条件。

假设现在有解。

我们对这些元素建线性基,并提取出**在基中的 \(S\) **,记为 \(in\)

\(in\) 大小等于 \(x\)。我们要构造一个二进制数的排列,相邻两个不同位置恰为 \(1\)每个数都代表选择 \(in\) 里的哪些

输出时可以用二进制数算出答案。

构造二进制数排列方法打个暴力找规律就会了。也可以去看别的题解。

\(in\) 这里讲的有些简陋。但如果你对前缀线性基里的引用段落完全理解,这一部分会很显然!

时间 \(O(\log^2 V\log n+V\log V)\)

我们发现,许多题都是从 \(in\) 这种思想开始思考的,通常会有双射,这也是线性基强大之处之一。

Code for this.


P4869 albus就是要第一个出场

我还没做完……qwq……


P4839 P 哥的桶


P5607 [Ynoi2013] 无力回天 NOI2017


CF959F Mahmoud and Ehab and yet another xor task