线性基
1 概念
线性基通常情况下指的是异或空间线性基。其标准定义是对于一个数集
由此我们可以得出下面两条重要性质:
-
原序列的任何一个数可以用线性基内部的一些数异或得到。
证明显然。
-
线性基内部任何数的异或不为
。证明考虑异或起来为
的几个数 与 ,可以得到的是 ,那么此时将 从线性基中删去仍不影响,就不满足线性基大小最小的条件了。所以异或值不可能是 。
下面我们来看线性基的基本操作与运用。
2 基本操作
2.1 构建
我们可以利用贪心法来构造线性基。假设我们前面已经构建好了线性基数组
不难发现的是,按照这样的插入方式,线性基上第
显然不管最后插入成功与否,我们都能保证
代码如下:
//#define int long long
int p[50];
void ins(int x) {
for(int i = 50; i >= 0; i--) {
if(!(x >> i)) continue;//第 i 位上为 0 直接判掉
//无需 &1 的原因是此时 x 比 i 位高的位一定都是 0 了
if(!p[i]) {//直接插入
p[i] = x; break;
}
x ^= p[i];
}
}
显然,插入线性基的复杂度是
2.2 求最大异或和
例题:【模板】线性基。
我们可以构造出原数组的线性基,然后我们只要在线性基上求出异或最大值就是原数组的异或最大值了。在线性基上求异或最大值显然可以贪心,我们尽可能让当前位上是
实际实现可以直接让
int query() {
int ans = 0;
for(int i = 50; i >= 0; i--) {
ans = max(ans, ans ^ p[i]);
}
return ans;
}
2.3 求最小异或和
我们只考虑线性基中的元素的话,最小值显然就是最小的有值的
代码过分简单,不放了。主要是没有例题。
2.4 线性基合并
这个东西其实非常简单,我们把一个线性基中的所有元素往另一个线性基里插一遍就完了。复杂度
基于这个我们就可以写出一个封装后的模板:
//#define int long long
struct Linear_Basis {
int p[61];
void ins(int x) {
for(int i = 60; i >= 0; i--) {
if(!(x >> i)) continue;
if(!p[i]) {
p[i] = x; break;
}
x ^= p[i];
}
}
Linear_Basis operator + (Linear_Basis b) {
Linear_Basis c = *this;
for(int i = 0; i <= 60; i++) {
if(!b.p[i]) continue;
for(int j = i; j >= 0; j--) {
if(!(b.p[i] >> j)) continue;
if(!c.p[j]) {
c.p[j] = b.p[i];
break;
}
b.p[i] ^= c.p[j];
}
}
return c;
}
};
3 进阶操作
3.1 求第 k 小 / 大异或和
这个就没有上面求极值那么简单了。我们以第
那么我们就可以将
但是接下来的问题就是,当我们的答案异或完一个值
那么我们对线性基进行重构,使得对于每一个最高位的
void rebuild() {
for(int i = 0; i <= 50; i++) {//暴力重构
for(int j = i - 1; j >= 0; j--) {
if((p[i] >> j) & 1) p[i] ^= p[j];
}
}
cnt = 0;
for(int i = 0; i <= 50; i++) {//记录有值的线性基值
if(p[i]) d[++cnt] = p[i];
}
}
那么查询之前先重构一次,然后按照刚刚讲的办法做即可。注意先特判有
int kth(int k) {
if(cnt < n) k--;//有 0
if((1ll << cnt) <= k) return -1;//线性基总数不够,直接返回
rebuild();//重构
int res = 0, pos = 1;
while(k) {//拆二进制,找 1 的位
if(k & 1) res ^= d[pos];//异或上
pos++;
k >>= 1;
}
return res;
}
不难发现对于动态插入和询问,求第
3.2 求排名
注意这里的排名指的是去重之后的排名,且要保证查询数可以被表示。不去重的排名见下面的例 1。
我们还是使用上面的
不过可以发现,上面的判断操作只和
代码如下:
int rnk(int x) {
int res = 0, mul = 1;
for(int i = 0; i <= 30; i++) {//枚举每一位
if(!p[i]) continue;//要保证 p[i] 有值
if((x >> i) & 1) {//x 这一位有值,加上 2^(k-1)
res += mul;
}
mul <<= 1;//记录当前的 2^k
}
res++;//如果要算空集的 0,这一行要加上
return res;
}
3.3 前缀线性基
这个东西可以在离线的情况下实现区间线性基的效果,并且复杂度基本复杂度还是
假如我们现在枚举到的右端点是
问题就在于当我们从
所以原理其实还是线性基的贪心构建思想,假如现在数字
代码如下:
void ins(int x, int p) {
for(int i = 50; i >= 0; i--) {
if(!(x >> i)) continue;
if(!p[i]) {
p[i] = x, pos[i] = p;
break;
}
else if(pos[i] < p) {
swap(p[i], x), swap(pos[i], p);
}
x ^= p[i];
}
}
3.4 图上线性基
其实这个图上线性基指的是一类用线性基求解的图论问题,他们的基本思想都是一致的。
首先要提到一道经典例题:[WC2011] 最大XOR和路径。
由于路径可能有重点重边,所以难以用最短路算法来求解。考虑到这个路径是异或起来的,所以我们只需要关注一条边走过的次数即可。考虑到我们从
于是可以得出一个重要结论:答案的权值是一条
然后考虑怎样求这个东西。首先看环,我们可以跑一边 DFS 树,只保留有一条非树边的环。可以发现,对于其它的不满足这个要求的环,一定可以用这些环异或得到,所以不需要考虑。
接着考虑
所以我们只需要将所有含一条非树边的环扔进线性基,然后查询
4 例题
例 1 albus 就是要第一个出场
发现这道题就是要求排名,但是换成了不去重的排名。
先用上文讲过的
那么任选数字的方案数应该有
例 2 [BZOJ3811] 玛里苟斯
题意: 求出一个可重集
由于异或较难处理,所以必须要拆位。我们要求的是
然后看到
考虑到
但是如果每一次都插入
时间复杂度
基于这个数据特点还有一种做法不难想到,由于
例 3 [BZOJ3569] DZY Loves Chinese II
这个题如果不强制在线的话理论上可以线段树分治乱做,但是有强制在线就会很难受。
首先会去考虑连通图怎样才能变成非连通图,即考虑要删掉哪些边。我们先随意建一棵 DFS 树,如果我们只删非树边的话一定不行,因为还有树边,所以至少要删一条树边。考虑删掉这条树边后怎样才能让这个树边的两端在两个连通块中,发现必须要让所有跨越它的非树边都被删掉。
现在的问题在于如何判断这个东西,考虑利用随机哈希,对非树边赋随机权值,每一条树边赋所有跨越它的非树边的权值异或和。那么如果删去的边集中有子集异或和为
判断是否有子集异或和为
例 4 [Beijing2011] 梦想封印
题意:给定一张无向图,单次删去一条边,定义一条路径的权值为经过的边的权值异或和。求从
我们需要运用上面讲过的有关图论的技巧,仍然考虑拆成链和环考虑。把环直接扔到线性基里,现在的问题就是从链中任选一条,和若干个环异或后能得到多少不同的值。
考虑这样一件事:对于两个数
那么我们对于所有的链就可以这样维护,把链扔到线性基中扫一遍,去重后计算答案即可。显然这可以用 set
维护,而最后的答案就是 set
的大小,
现在考虑如何动态维护这个东西。我们先套路的删边改加边,然后分类讨论:
- 如果
均与 相连,加入这个环即可。 - 如果
有一个与 相连,暴力跑一遍另外一个点对应的图,跑出一棵 DFS 树然后将上面对应的环和链加入即可。 - 如果
均不与 相连,只连边即可。
注意每次插入数字到线性基中后要重构一边 set
。时间复杂度在于重构的复杂度,最多重构
例 5 [SCOI2016] 幸运数字
首先看到这是个树上问题,求异或最大值,第一个想到的肯定是树剖。但是为了维护异或最大值的话我们需要线性基,并且树剖后查的还是区间线性基,需要用线段树维护线性基。这样做的复杂度是
考虑处理树上问题的另一种方式,即倍增。预处理
但实际上理论复杂度最优的解法不止于此。考虑线性基合并是具有可重复贡献性的,所以不需要保证插入的线性基在树上是不重合的。因此可以借鉴
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律