[笔记] 线性基
用处
- 快速查询一个数是否可以被一堆数异或出来
- 快速查询一堆数可以异或出来的最大 / 最小值
- 快速查询一堆数可以异或出来的第 \(k\) 大值
性质
- 原数列里的任何一个数都可以通过线性基里的数异或表示出来
- 线性基里任意一个子集的异或和都不为 \(0\)
- 一个数列可能有多个线性基,但是线性基里数的数量一定唯一,而且是满足性质一的基础上最少的。
实现
构造
void insert(LL x){
rfor(i, lim, 0) if(x & (1LL << i)){
if(a[i]) x ^= a[i];
else{ a[i] = x; break; }
}
}
查询一个元素是否可以被异或出来
bool check(LL x) {
rfor(i, lim, 0) if(x & (1LL << i)) x ^= a[i];
return x == 0;
}
查询异或最大值
按位贪心.
LL askmax() {
LL ans = 0;
rfor(i, lim, 0) if(ans < (ans ^ a[i])) ans ^= a[i];
return ans;
}
查询异或最小值
一般来说就是线性基里的最小元素,特判 \(0\).
LL askmin() {
if(zero) return 0;
lfor(i, 0, lim) if(a[i]) return a[i];
}
查询异或第 \(k\) 小
重建线性基,使得不同位尽量不相互影响。
void rebuild() {
rfor(i, lim, 0) rfor(j, i - 1, 0) if(a[i] & (1LL << j)) a[i] ^= a[j];
lfor(i, 0, lim) if(a[i]) p[++cnt] = a[i];
}
类似树状数组二分第 \(k\) 小,同样特判 \(0\).
LL kth(int k) {
LL ans=0;
rfor(i, lim, 0) if(k & (1LL << i)) ans ^= p[i];
return ans;
}
查询排名
与查询第 \(k\) 小类似.
int rank(LL x) {
LL ans = 0;
rfor(i, cnt - 1, 0) if(x >= p[i]) ans += (1LL << i), x ^= p[i];
return ans + zero;
}
题目
从 \(1-n\) 的最大XOR和路径可以看作任意一条从 \(1-n\) 的路径异或上若干个环。
分析出答案的补集就是线性基。