线性基小记
线性基(这里是异或线性基)是对于序列
-
中的所有元素可以通过异或表示出 中的所有元素。 -
在满足第一个条件的情况下,集合大小最小。
进一步的,可以推出以下性质:
-
中任意元素的异或和不等于 。 -
的大小至多为 。
第二个性质将在插入时自然给出。
基本操作:
- 插入:假设现在要插入的数是
。考虑记 是以第 位为最高 位的基底。不难发现,假如当 遍历到第 位时,且 的第 位为 ,该位的 为空,则 无法被表出,原因是再向下遍历则无法在更改这一位。否则 必须要异或上该位。若最后 变为 ,其实就是 已经可以被表出,跳过即可。
qwq
void ins(int x){ for(int i = M; i >= 0; i--){ if(!(x & (1ll << i))) continue; if(!bs[i]){bs[i] = x; return;} x ^= bs[i]; } }
- 查询最大值:假设现在要查询
中选出任意个数与 的异或最大值,从高位向低位考虑,显然后面选择的不会影响前面的,直接贪心即可。
qwq
int getmax(int x){ int ret = x; for(int i = M; i >= 0; i--) ret = max(ret, ret ^ bs[i]); return ret; }
- 查询第
小的值:考虑类似于 上二分的做法,首先将所有的 变成任意两个都无交的形式,从高位向低位考虑,容易发现,假设这一位选择 ,则排名区间就变成了 ,否则是 。然后二分就完了。
qwq
void rebuild(){ for(int i = 0; i <= M; i++){ for(int j = i - 1; j >= 0; j--){ if(bs[i] & (1ll << j)) bs[i] ^= bs[j]; } } tot = 0; for(int i = 0; i <= M; i++) if(bs[i]) rbs[++tot] = i; } int kth(int x, int k){ int ret = x; for(int i = tot; i >= 1; i--){ int sz = (1ll << i - 1); if(k <= sz){ if((x & (1ll << rbs[i]))) ret ^= bs[rbs[i]]; } else{ if(!(x & (1ll << rbs[i]))) ret ^= bs[rbs[i]]; k -= sz; } } return ret; }
随机说话:
-
线性基具有可合并性,是
的,还算可以,大概可以用线段树之类的维护?(所以是不是可以出一个线性基+猫树分治qwq) -
如果一个数无法插入到线性基中,说明有一个子集的异或和为
(显然的qwq) -
最大异或路径就是随便跑一个生成树,然后再走若干个环就行了。因为你不难发现,假如要使路径中包含一个环,可以走过去绕一圈原路返回,中间的都被消掉了,这样只需要加入环的贡献,于是可以直接加入进路径异或和中。如果不是原路返回,也就是说这里有两条路径从环到原路径,于是等价于再加入一个环的贡献。假如你是要走另外一条链,那么一定会和原来的路径构成一个环,于是也包含在其中了。
这里运用到一个经典的转化:两个点有两条路径相互到达
两个节点中可达的部分一定有一个环
-
假如一个线性基里面有
个元素,可以表出的不同数的个数为 。进一步的,假如线性基中有包含第 位的元素,会有 个可以表出的数在该位上为 ,剩下 个在该位上为 。这一点是由于可以把线性基中的元素变为两两不交的形式,然后强制选出/不选含有该位的那个元素,剩下随便选。 -
二进制题目的基本套路:高位贪心,按位统计
练习题
P4570 [BJWC2011]元素
P3292 [SCOI2016]幸运数字
P4151 [WC2011]最大XOR和路径
P5556 圣剑护符
CF724G Xor-matic Number of the Graph
CQOI2013 新Nim游戏
CF938G Shortest Path Queries
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18554503
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步