线性基

线性基

纯 oi 讲解,可以当作数据结构来看,涉及到了 0 个线代。

定义

对于原集合或序列 \(a\),我们定义集合 \(s\) 为其线性基,那么 \(s\) 中的数应满足下面性质:

  • \(s\) 中的数亦或不出来 \(0\)
  • \(s\) 可以亦或出 \(a\) 能亦或出的所有数,不能亦或出 \(a\) 亦或不出来的数。
  • 线性基具有唯一性,对于能亦或出的数字 \(x\),设 \(t \subset s\) 中所有数亦或和为 \(x\),那么 \(t\) 是唯一的。

可以证明线性基只有 \(\log V\) 个元素,其中 \(V\) 表示值域。

构造

先说每个位置的意义。我们用 \(c_i\) 表示线性基中的每一个数字。第 \(i\) 位表示基中最高位为 \(i\) 的数,没有则为 \(0\)

线性基支持动态的插入,不支持删除。对于我们插入了一个 \(x\),从高往低枚举位 \(i\)。若此时 \(x\) 最高位就为 \(i\),若 \(c_i = 0\)\(c_i \gets x\),否则 \(x \gets x \oplus c_i\)。写成代码长这样:

void ins(int x) {
    for(int i = maxk - 1; i + 1; i--) {
        if((x >> i) & 1) {
            if(c[i]) x ^= c[i];
            else {
                c[i] = x;
                AC;
            }
        }
    }
}

特殊的,当枚举完了之后 \(x = 0\),那么证明 \(x\) 能被亦或出来,我们称其为插入失败。

我们想建出集合 \(a\) 的线性基,只需要将其中元素一个一个插入即可。

运用

十分灵活。

  • 对于集合 \(a\),快速判断其内部元素能否亦或出 \(x\)

首先对 \(a\) 建出线性基 \(s\),然后从高到低位枚举 \(i\)。若当 \(x\) 最高位就是 \(i\),那么 \(x \gets c_i\)。枚举完之后,若 \(x = 0\) 那么 \(x\) 就能被亦或出来,\(x \ne 0\) 反之。

  • 对于数字 \(x\),求其和任意若干个 \(a\) 中的数亦或后的最大值。

建出线性基,从高到低枚举位数。加入当前位数为 \(i\),若 \(x \oplus c_i > x\) 那么 \(x \gets c_i \oplus x\)。弄完之后的 \(x\) 就是答案。

  • 线性基能异或出的最小值。

最小的 \(c_i \ne 0\)

  • 原序列能异或出的最小值

若出现插入失败则为 \(0\),否则还是最小的 \(c_i \ne 0\)

  • 对于集合 \(a\),求其所有能亦或出来的数之和。

拆贡献到每一位。先建出线性基。设 \(t\) 表示有多少个 \(c_i \ne 0\)。那么对于每一个有 \(c_i \ne 0\)\(i\),都有贡献 \(2 ^ i \times 2 ^ {t - 1}\),总结一下答案为:

\[\sum_{i = 0} ^ {\log V} [c_i \ne 0] 2 ^ i 2 ^ {t - 1} \]

  • 套线段树。

具体可以看这道题

posted @ 2026-07-05 18:42  ACehomoxue  阅读(7)  评论(0)    收藏  举报