(二进制)线性基
jiazhaopeng · · 个人记录
觉得二进制线性基又是一个数据结构,因此就单独搬出来了。
详见:线性基详解
线性基的构造
d[i]存着一个线性基,它的最高位为第i位。(d[i]可能为空)
inline void ins(ll num) {
for (register int i = 52; ~i; --i)
if ((num >> i) & 1) {
if (!d[i]) return d[i] = num, void();
num ^= d[i];
}
}
找最大异或和
即这道例题:P3812 【模板】线性基
贪心:由于线性基就是一些数异或得到的,因此设答案res,从高位到低位扫线性基数组,如果能使res更大,就让res更大。
ll res = 0;
for (register int i = 52; ~i; --i)
if ((res ^ d[i]) > res)
res ^= d[i];
但是有时要开好多线性基,连35的数组都开不下,这时就需要用vector了。
注意到每次能把 x 加入线性基,当且仅当 x 不能被d[] 中的数线性表出(无法被异或成0)。因此我们每次贪心异或(尽可能往小里异或),如果最后非0就加入,同时维护有序性。
查最大就像往常一样,从大到小贪心即可。
struct line{//线性基
vector<int> d;
inline void add(int x) {
for (register int i = 0; i < d.size(); ++i)
if ((x ^ d[i]) < x) x ^= d[i];
if (!x) return ;
d.push_back(x);
for (register int i = d.size() - 1; i; --i) {//slow sort
if (d[i] > d[i - 1]) swap(d[i], d[i - 1]);
}
}
inline int get_max() {
int res = 0;
for (register int i = 0; i < d.size(); ++i)
if ((res ^ d[i]) > res) res ^= d[i];
return res;
}
};
注意!!!
记得判(num >> i) & 1 !!!
第二种线性基写法不能改成d数组递增的形式,我也不知道为什么。
这样插的线性基符合三条性质:
线性基三大性质
1.原序列里面的任意一个数都可以由线性基里面的一些数异或得到
2.线性基里面的任意一些数异或起来都不能得到0
3.线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的
- by ryc学长课件,上面博客也有写到
线性基的应用
找最小异或和
贪心:如果之前插入时失败过,就为0,否则最小异或和就是最小的d[i]。
找第k小异或和
首先把所有的d都搞成"1xx0xxxx0xxx0xx"的样子(0表示那一位可以被异或成为0)(此时d仍然为线性基)。
然后把k拆成二进制,并从小到大扫d数组。如果d[i]非0(那一位可以自由选择),就根据k的最后一位情况决定是否异或上d[i]。
因为d[i]代表第i位是否为1,那么只要我们把d[i]是否异或和k的二进制对应起来,答案就是正确的。
注意判断一下异或和为0是第一小的情况。
代码大概长这样:
for (register int i = 52; ~i; --i){if (d[i])
for (register int j = i - 1; ~j; --j)
if (d[j] && d[i] & (1ll << j))
d[i] ^= d[j];
ll res = 0;
for (register int i = 0; i <= 52; ++i)
if (d[i]) {
if (k & 1) res ^= d[i];
k >>= 1;
}
线性基的删除
由于考的不是很多,再加上那篇博客讲得不是很清楚,因此没学。
一些例题
- P4570 [BJWC2011]元素
给带权的一些数,求最大权二进制线性无关组。
解决这种问题的常见方法还是贪心:
反正线性基的数量唯一,就把元素按权从大到小排序,挨个插进去,能插进去就计入答案。这样保证答案最大。
- BZOJ4184: shallot
需要线段树分治,详见:线段树分治