标签:http 方案 return htm 个性 通过 两种 标准 void
u1s1 这是我第三次学这个东西了,第一次大约是去年三月,第二次大约是去年暑假,事实证明那时候并没有把这东西真正学进去因为我那时并没有真正理解线性基的本质,现在学完高斯消元,学完线性代数的一些基本芝士再回来看这东西就感觉异常友好了(
一个 \(n\) 维向量是一个 \(n\) 元有序数组 \(v=(a_1,a_2,\cdots,a_n)\),记作 \(v\in\mathbb{R}^n\)。
在线性基中我们一般只需用到向量的加减与数乘运算,也就是说对于向量 \(u=(a_1,a_2,\cdots,a_n),v=(b_1,b_2,\cdots,b_n)\),我们定义以下运算:
对于向量集 \(V=\{v_1,v_2,\cdots,v_n\},v_i\in\mathbb{R}^m\) 和向量 \(x\in\mathbb{R}^m\),如果 \(\exist c_1,c_2,\cdots,c_n\in R\) 满足 \(c_1v_1+c_2v_2+\cdots+c_nv_n=x\),则称 \(x\) 为 \(V\) 以 \(c_1,c_2,\cdots,c_n\) 为权的线性组合。所有这样的 \(x\) 组成的集合称为 \(V\) 的张成空间,记作 \(\text{span}(V)\)
关于这东西最原始的定义是,一个向量集 \(V=\{v_1,v_2,\cdots,v_n\},v_i\in\mathbb{R}^m\) 为线性无关集,当且仅当方程 \(v_1x_1+v_2x_2+\cdots+v_nx_n=0\) 的唯一解为 \(x_i=i,i\in[1,n]\)。反之称其为线性有关集。
当然还有不少等价的定义,一个比较常用的定义是向量集 \(V=\{v_1,v_2,\cdots,v_n\}\) 为线性有关集,当且仅当 \(\exist j\in[1,n]\) 满足 \(v_j\) 可以表示为 \(v_1,v_2,\cdots,v_{j-1}\) 的线性组合。
一个小性质是若 \(n>m\),则 \(V\) 必然为线性有关集,证明可用高斯消元,这里就不再赘述了。
若向量集 \(B\) 线性无关,则称 \(B\) 是 \(\text{span}(B)\) 的一组基。
基有如下一些性质:
\(B\) 的任何一个真子集都不是 \(\text{span}(B)\) 的基(提示:反证法)
对于 \(\text{span}(B)\) 中任意一个向量 \(v\) 都存在唯一的 \(c_1,c_2,\cdots,c_{|B|}\) 使得 \(v\) 为以 \(B\) 以 \(c_1,c_2,\cdots,c_{|B|}\) 为权的线性组合(提示:反证法+做差,推出 \(B\) 线性有关)
对于线性有关集 \(V\),存在 \(B\subset V\) 且 \(\text{span}(B)=\text{span}(V)\)(提示:根据线性有关集的定义)
在 OI 中,当我们碰到一类与异或和有关的问题时,常常可以考虑使用线性基,因为如果我们把每个数看作一个 \(32\)(或 \(64\) 位)每一维取值为 \(0/1\) 的向量,那异或运算即是 \(\bmod 2\) 意义下的向量加法,这样即可转化为线性代数问题。为了表述方便,下设位数为 \(B\),即 \(\lfloor\log_2\max\{a_i\}\rfloor\)。
仿照上面张成空间的定义,我们同样定义一个整数序列 \(a_1,a_2,\cdots,a_n\) 的张成空间 \(\text{Span}(a)\) 为:考虑将所有 \(a_i\) 的二进制表示看作一个每一位取值为 \(0/1\) 的向量 \(v_i\),那么 \(\text{Span}(a)\) 即为向量集 \(\{v_1,v_2,\cdots,v_n\}\) 在 \(\bmod 2\) 意义下的张成空间。其实际意义是 \(a\) 序列所有子序列(可以为空)的异或和组成的集合。再定义其线性基为 \(\text{Span}(a)\) 的一组基,显然,一个序列的线性基并不唯一,假设 \(b_1,b_2,\cdots,b_m\) 为 \(a_1,a_2,\cdots,a_n\) 的一组基,那么你随意选择某个 \(i,j\) 并令 \(b_i\leftarrow b_i\oplus b_j\),得到的 \(b_1,b_2,\cdots,b_m\) 仍为 \(a_1,a_2,\cdots,a_n\) 的基。
那么怎么求一个序列的张成空间呢?我们考虑这样的算法,动态维护一个集合 \(S\),对于每一个向量(整数),我们查看其是否可以表示为当前 \(S\) 中的元素的线性组合(异或和),如果是则跳过,否则将其加入 \(S\),最终 \(S\) 即为 \(a_1,a_2,\cdots,a_n\) 的一组线性基。为什么这个算法是正确的呢?首先根据上面基的第三个结论可知,必然 \(\exist S‘\subseteq S\) 满足 \(S‘\) 为 \(a_1,a_2,\cdots,a_n\) 的线性基。而根据线性有关集的等价定义,当我们依次加入向量的时候,如果加入了某个向量,满足其可以表示为之前加入的向量的线性组合,那么新加入这个向量之后,向量集就不是线性无关集了,也就不满足线性基的定义了。并且对于 \(\forall x\),如果 \(x\) 可以表示为原本 \(a_1,a_2,\cdots,a_n\) 的线性组合,那么必然 \(\exist c_1,c_2,\cdots,c_n\) 满足 \(c_1a_1+c_2a_2+\cdots+c_na_n=x\)(这里的 \(+\) 为向量的加法,即二进制下的异或),而对于所有被扔掉的向量 \(i\),\(a_i\) 又可表示为不被扔掉的向量之和,也就是说 \(c_ia_i\) 这一项可展开为不被扔掉的向量之和的形式,故 \(x\) 也可表示为 \(S\) 的线性组合,符合题意。
最后问题就是怎么检验某个元素是否可以表示为 \(S\) 中元素的线性组合。一个非常直观的想法是高斯消元,对于一个待检验的向量 \(v\),我们考虑 \(v\) 中的最高位 \(b\),如果 \(S\) 中存在某个向量 \(v‘\) 满足 \(v‘\) 的最高位为 \(b\),就用 \(v‘\) 去消 \(v\),即 \(v\leftarrow v‘\oplus v\),如果最后 \(v‘\) 消到 \(0\) 则可以表示为 \(S\) 中元素的线性组合,反之 \(v\) 不可以表示为 \(S\) 中元素的线性组合。
具体实现的时候,由于异或运算的特殊性,我们并不用按部就班地写 \(B^3\) 的高斯消元。我们建一个数组 \(b\),\(b_i\) 表示最高位的 \(1\) 为第 \(i\) 位的向量,当我们加入一个数 \(v\) 的时候,就从高位到低位枚举,每枚举到一个当前位为 \(1\) 的位置 \(p\),就令 \(v\leftarrow v\oplus b_p\),把这一位上的 \(1\) 消掉,特别地,若 \(b_p=0\),则表明这一位上的 \(1\) 消不掉,就令 \(b_p\leftarrow v\),表示将 \(v\) 插入线性基中,如果到了最后一位还没被插进去,则表明 \(v\) 插不进去。时间复杂度 \(nB\),正确性显然。
void ins(ll x){
for(int i=MAXB;~i;i--) if(x>>i&1){
if(a[i]) x^=a[i];
else{a[i]=x;return;}
}
}
对于数集 \(S\),我们要求 \(S\) 的所有子集可以得到不同的异或和个数,也就是集合 \(\text{Span}(S)\) 的大小。这里有一个经典的结论,就是 \(|\text{Span}(S)|=2^{|\text{Base}(S)|}\),其中 \(\text{Base}(S)\) 为 \(S\) 的线性基。假设 \(\text{Base}(S)=\{b_1,b_2,\cdots,b_m\}\),考虑序列 \(x_1,x_2,\cdots,x_m,x_i\in[0,1]\),记 \(V(x)=x_1b_1\oplus x_2b_2\oplus\cdots\oplus x_mb_m\),如果存在两个不同序列 \(x_1,x_2,\cdots,x_m\) 以及 \(x‘_1,x‘_2,\cdots,x‘_m\) 满足 \(V(x)=V(x‘)\),考虑做差,\((x_1-x‘_1)b_1\oplus(x_2-x‘_2)b_2\oplus\cdots\oplus (x_m-x‘_m)b_m=0\),由于 \(x,x‘\) 不同,必然 \(\exist i,x_i\ne x‘_i\),此时 \(x_i-x‘_i\ne 0\),也就是说关于 \(b_1,b_2,\cdots,b_m\) 的方程 \(x_1b_1\oplus x_2b_2\oplus x_3b_3\oplus\cdots\oplus x_mb_m=0\) 除 \(x_i=0\) 外还存在别的解,与 \(b_1,b_2,\cdots,b_m\) 为线性无关集矛盾。因此对于任意序列 \(x_1,x_2,\cdots,x_m,x_i\in[0,1]\),\(V(x)\) 都互不相同,故 \(|\text{Span}(S)|=2^{|\text{Base}(S)|}\)(其实也就是前置芝士中基的第二个性质,这里只不过将其完整地证了一遍)
当然如果要求集合非空的话还要特判 \(0\) 是否能被表示出来。如果 \(S\) 本身就是线性无关集,那么 \(0\) 表示不出来,须减去 \(1\)。否则答案还是 \(2^{|\text{Base}(S)|}\),至于怎么检验 \(S\) 是不是线性无关集……只需检验是否存在插不进去的 \(a_i\) 即可。
最大异或子集:考虑从高到低枚举,在枚举过程中维护答案 \(ans\),假设当前枚举到第 \(p\) 位,如果 \(ans<ans\oplus b_p\),则令 \(ans\leftarrow ans\oplus b_p\),否则不做任何操作。最终的 \(ans\) 即为最大异或子集。考虑每次枚举到的状态,根据 \(b\) 的定义及线性基的求解过程可知若 \(b_p\ne 0\),那么 \(b_p\) 二进制下第 \(p\) 位必然为 \(1\),这里分两种情况考虑,若枚举到 \(p\) 时 \(ans\) 二进制下第 \(p\) 位为 \(1\),则 \(ans\oplus b_p\) 后二进制第 \(p\) 位为 \(0\),并且以后不管怎么取都补不会这一位上的 \(1\),因此我们令 \(ans\) 保持不变。否则\(ans\oplus b_p\) 后二进制第 \(p\) 位为 \(1\),我们就令 \(ans\leftarrow ans\oplus b_p\)。
最小异或子集:这个就很 trival 了,直接输出最小的 \(b_p\) 就行了,因为它不管与什么异或都会变得更大。
大体思路和本质不同子集异或和个数差不多罢……只不过把求异或和个数改为第 \(k\) 小的异或和了而已……
首先我们假设有值的 \(b_p\) 从低位到高位分别为 \(c_0,c_1,c_2,\cdots,c_t\),一个非常 naive 的想法是对 \(k\) 进行二进制拆分,如果 \(k\) 的第 \(i\) 位为 \(1\),那么令 \(ans\leftarrow ans\oplus c_i\)。但是这样很明显是错误的,举个例子,\(c_0=1,c_1=3,k=2\),那么运行得出的结果为 \(3\),可实际答案为 \(2\)。
为什么会出现这样的情况呢?因为我们无法保证对于一个状态 \(ans\),用其去异或一个没有异或过的 \(b_i\) 之后,无法保证异或和严格增大,也就无法保证它是第 \(k\) 小的了。那么有什么解决办法呢?考虑在插入的时候加个小小的操作,譬如我们要令 \(b_p\leftarrow x\),那么我们枚举 \(i=0,1,\cdots,p-1\),如果 \(x\) 的第 \(i\) 位为 \(1\),那么令 \(x\leftarrow x\oplus b_i\),再枚举 \(i=p+1,p+2,\cdots,B-1\),如果 \(b_i\) 的第 \(p\) 位为 \(1\),那么令 \(b_i\leftarrow b_i\oplus x\)。不难发现这样操作完之后,对于 \(\forall i\),都有 \(b_{i+1},b_{i+2},\cdots\) 的第 \(i\) 位不是 \(1\)。这样一来当我们枚举到 \(p\) 的时候,不论 \(b_{p+1},b_{p+2},\cdots,b_{B-1}\) 是否选择,都有 \(ans\oplus b_p>ans\)(这里假设 \(b_p\ne 0\)),并且后来这一位上的 \(1\) 肯定也无法被消除了,也就是说选择 \(b_p\) 总比不选 \(b_p\) 大,因此再跑一遍上面的操作即可。这样插入复杂度变成了 \(B^2\),总复杂度 \(\mathcal O(nB^2)\)。
顺便说一句,这种插入方法插得的线性基被称为上三角基,其与普通线性基的区别类似于阶梯型与简化阶梯型的区别。
这个就没什么好说了罢,暴力将一个线性基中的元素插入另一个线性基即可,由于线性基大小最多为 \(B\),因此复杂度为 \(\mathcal O(B^2)\)
这东西一般不太常见罢……
可以配合线段树分治做到 \(nB\log n\) 的复杂度,一般这样的复杂度就足够。
据说这东西可以强制在线?不管了,先咕着了……
一排鸽子缓缓飞过 ????????????????????????????????????????????????????????????????????????
具体参见下面的例题 9 P4151 [WC2011]最大XOR和路径,一般用于求解一类与图上非简单路径的异或值有关的问题。
大概也就是把线性基扩展到实数域罢……
将所有装备按价格从小到大排序,动态地维护集合 \(S\),当我们插入一个装备时,查看其是否可以表示为 \(S\) 中的装备的线性组合,如果可以则不插入,否则将其插入 \(S\),答案加上其价格。正确性同 Kruskal。
这不就本质不同子集异或和个数的模板吗
直接把所有灯看作一个二进制数插入线性基,然后输出 \(2\) 的线性基大小次方即可。
u1s1 每次在洛谷搜索题目界面输入题号都会跳出来这题,这次终于真正做到这题了
如果 \(l=1,r=n\) 那就是一个标准的最大子集异或和的板子,每插入一个数就将其插入线性基,然后从线性基中查询最大子集异或和即可。
加上区间怎么办呢?考虑建一棵线段树,线段树每个节点维护其代表的区间中元素的线性基即可,单点修改就将待修改的值插入根到叶子节点路径上每个节点的线性基中,区间查询就执行 \(\log n\) 次线性基合并,复杂度 \(n\log^3n\),可通过此题。
一个非常自然的想法是想上一题一样线段树+线性基,复杂度 3log。u1s1 这数据范围连 2log 都不愿意放过去你 3log 就更别想了(虽然这题似乎 2log 的确能过?
我们考虑对序列的每个前缀 \(a_1,a_2,\cdots,a_i\) 各建一个线性基。只不过线性基中额外再维护一个值 \(pos_i\) 表示能够使这一位为 \(1\) 的最大的位置,那么 \([l,r]\) 中的数构成的线性基就是前缀 \([1,r]\) 的线性基中 \(pos_p\ge l\) 的部分。
考虑怎样维护这个 \(pos\)。我们每插入一个值 \(x=a_p\),那么如果有一位的 \(pos<p\),那么我们就把这一位的 \(b\) 取出来换成 \(x\),\(pos\) 换成 \(p\),然后把交换后的 \(x,p\) 继续往下插入,这样构造出的线性基 \(pos\) 就是最大的了。
时间复杂度 \(n\log n\)。
一个想法是线段树+树剖+线性基,但这样你的复杂度会达到惊人的 \(n\log^4n\),众所周知这样复杂度的算法跑不过暴力(((
考虑将树剖换成功效类似(bushi)的倍增,对每个节点 \(u\in[1,n],k\in[0,\log_2n]\) 建一个线性基 \(b_{u,k}\),分别维护 \(u\) 及 \(u\) 的祖先,\(u\) 的 \(2\) 级祖先,……,\(u\) 的 \(2^k-1\) 级祖先权值的线性基,查询就暴力跳父亲并合并线性基即可。这样预处理时须进行 \(\log n\) 次线性基合并,单次查询也需进行 \(\log n\) 次线性基合并,复杂度 \((n+m)\log^3n\),还是无法通过此题。
注意到线性基合并这个东西与 \(\min,\max\) 类似的一点是,它属于可重复计算贡献的量,也就是说当你插入某个值 \(x\) 时候,再重新插入 \(x\) 时线性基保持不变,即其满足自反性 \(x+x=x\)。考虑借鉴 ST 表的思想,对于 \(u\) 到 \(u,v\) 的 LCA \(w\) 这条链上的点,记 \(k=\lfloor\log_2(dep_u-dep_w+1)\rfloor\),我们只需将 \(b_{u,k}\) 与 \(b_{fa_{u,dep_u-dep_w+1-2^k},k}\) 合并即可,其中 \(fa_{u,k}\) 为 \(u\) 的 \(k\) 级祖先。这样即可实现 \(\mathcal O(n\log^3n)-\mathcal O(\log^2n)\)。
复杂度 \(n\log^3n+m\log^2n\)。
看到这个数据范围与操作类型,一眼线性基+线段树,然鹅你码了个带 pushdown
,区间修改打标记的线段树,却发现连样例都过不了?
为什么?因为线性基本来就不支持整体加。也就是说对于 \(a_1,a_2,\cdots,a_n\) 和 \(k\in R\),\(a_1,a_2,\cdots,a_n\) 的线性基 \(\ne\) \(a_1\oplus k,a_2\oplus k,\cdots,a_n\oplus k\) 的线性基。
既然区间修改搞不了,那咱就另辟蹊径呗。考虑有个东西叫差分,我们记 \(b_i=a_i\oplus a_{i-1}\),那么区间异或相当于修改 \(b_l,b_{r+1}\) 的值。噫,好了!这样我们就将区间异或转化成了单点异或,也就避开了打异或标记这一步。
那怎么求 \(a_l,a_{l+1},\cdots,a_{r}\) 的线性基呢?一个性质是 \(a_l,a_{l+1},\cdots,a_r\) 的线性基就是 \(a_l,b_{l+1},\cdots,b_r\) 的线性基,因为 \(a_i=\operatorname{xor}\limits_{j=l+1}^ib_j\oplus a_l,i\in(l,r]\),并且由于异或操作是可逆的,故所有可表示为 \(a_l,a_{l+1},\cdots,a_r\) 的数都可表示为 \(a_l,b_{l+1},\cdots,b_r\),反之亦然。而 \(b_{l+1},b_{l+2},\cdots b_r\) 的线性基是显然可以线段树维护的,于是这题就这么做完了。复杂度 3log。
Yet another mol ban tea?
求出 \(a\) 序列的线性基,将其削成上三角基,然后二进制随便搞一搞即可。
为啥这题目名字这么长?
对 \(a\) 序列的每个前缀建线性基,每次询问就判断 \(x\) 是否可以通过 \(a_1,a_2,\cdots,a_l\) 的线性基中的数表示出来,如果不可以则输出 \(0\),否则假设 \(a_1,a_2,\cdots,a_l\) 线性基大小为 \(c\),那么答案为 \(2^{l-c}\),因为线性基有个性质,就是对于所有能够表示成 \(a_1,a_2,\cdots,a_n\) 的某个子集的异或和的数 \(x\),满足 \(S\subseteq\{a_1,a_2,\cdots,a_n\}\) 且 \(\operatorname{xor}\limits_{x\in S}x\) 的 \(S\) 的个数都是相同的,都等于 \(2^{n-\text{线性基大小}}\)
首先发现所有合法的答案肯定可以通过通过随便一条由 \(1\) 到 \(n\) 的路径异或上图中若干个环得到。
构造方案的话只需要从 \(1\) 号点走到环上任意一点,转完整个环,再回到 \(1\) 号点,会发现只有环上的值被取了,其他路径上的值都被异或抵消了。
考虑随便找一棵以 \(1\) 为根的生成树,显然图中的边只可能有两种类型——树边和非树边,对于每条非树边 \((u,v)\),显然其与生成树上 \(u\to v\) 的路径所经过的边会形成一个环,将这个环的权值插入线性基即可。最后随便找一条 \(1\to n\) 的路径,设其权值为 \(w\),在线性基中查询与 \(w\) 异或值最大的子集即可。
复杂度 \(n\log n\)。
上一题的带修版本。。。*2900 的 G 题也被我搞出来了(这大概是第 7 道我自己搞出来的 *2900 了罢)
考虑线段树分治,对时间轴建一棵线段树。将每条边存活拆成 \(\log n\) 个小区间插入线段树。然后对整棵线段树进行一遍,可撤销冰茶姬维护连通性,具体来说当 DFS 到某个节点时就将它上面每条边的两个节点合并起来,如果它们本来就属于一个连通块就说明会形成一个环,将环的权值插入线性基,权值可通过在冰茶姬中额外维护每个点到其连通块所在根节点路径上的权值的异或和得到。
复杂度 \(n\log^2n\)。
……有什么意义吗?
感觉跟上一题几乎一模一样,就没啥好说的了吧。
然鹅这题非常精神污染的是要自己手写高精度类,不过由于是二进制,并且只用实现异或操作,所以实现起来没那么复杂,维护个 bitset
,并用 bitset
自带的异或操作即可。
时间复杂度 \(\dfrac{qL^2\log q}{\omega}\)。
一言以蔽之,bitset yyds!
nb tea,题解
本题等价于求 \(u\) 到 \(v\) 路径上经过的点的权值组成的集合是否为线性无关集。
于是我们又有了一个高复杂度的暴力做法,树剖+线段树+线性基合并,yet another \(n\log^4n\),yet another 过不去
注意到线性无关集有一个性质,就是如果集合大小 \(>\) 向量维数那么该集合一定不是为线性无关集。将此结论应用到此题来,如果 \(u,v\) 路径上的点数 \(\ge\log v\),那么该集合一定是线性有关集,直接输出 YES
即可。对于剩余的情况,也就是 \(u,v\) 路径上的点数 \(<\log v\) 直接暴力跳 father 并将权值插入线性基即可,修改可用树剖维护,复杂度 \(n\log^2n\),可以通过此题。
Yet another nb tea,题解
当碰到异或、子序列、非简单路径等字眼时,可以想到用线性基解决。
线性基与 01 trie 等维护异或的其他数据结构有本质区别,注意区分。
标签:http 方案 return htm 个性 通过 两种 标准 void
原文地址:https://www.cnblogs.com/ET2006/p/linear-base.html