zoukankan      html  css  js  c++  java
  • 【模板】线性基

    线性基

    定义:给定数集(S),以异或运算张成的数集与(Span{S})相同的极大线性无关集,称为原数集的一个线性基。
    线性基具有以下性质:

    1. 显然,线性基是原数集的一个子集。
    2. 线性基的张成集合中一般不包含有数字0。一般给定的数集中不会有0,否则在线性基中加入0即可。
    3. 张成集合中的每个数都可以唯一表示成基的线性组合。这与第一点是统一的。
    4. 将线性基中的一个元素通过“倍加变换”(在这里即异或上另一个元素),张成集合不变。
    5. 线性基中每一个元素的最高位均不同。

    由以上性质不难看出,线性基与线性方程组有一些相似之处。事实上,如果把原数集的每一个元素二进制拆分为多项式,把每一项的系数看作一个向量,将所有系数向量看作一个矩阵的行,那么这个构造出的矩阵就可以看作某个线性方程组的系数矩阵。也就是说,对这个矩阵施以异或意义下的初等行变换,即将某些行消成0行,形成的新矩阵与原矩阵行等价。这给了性质5一个很好的阐释:每个元素的最高位对应的是该系数矩阵化为阶梯形后的主元位置,而主元可以将其他行的该项通过行变换消去,结合异或的性质就不难理解了。
    以上基于线性代数的讨论表明,可以使用类似高斯消元的行化简算法求得原数集的一个线性基。把每个行向量压成一个数处理,时间复杂度(O(n^2))
    具体来说,对于每一个新加入的数x,我们从高位到低位枚举;如果x的第i位是1(即对应方程左边含有这个元素),并且之前没有出现过该位是1的元素,我们就将x插入线性基(即以这一行作为主元(x_i)的所在行);否则将x异或掉之前出现过的p[i],继续看下一位,直到x被插入或x变为0(即x可以表示为已有基的线性组合)结束。

    插入函数代码:

    void insert(long long x) {
        for (int i = 60; i >= 0; --i) 
            if ((x >> i) & 1) {
                if (!p[i]) {
                    p[i] = x; break;
                }
                x ^= p[i];
            }
    }
    

    luoguP3821
    (题解引自洛谷)

    先用高斯消元求得一组线性基。由高斯消元可得,该线性基中,主元所在位为1的向量是唯一的。接下来我们可以从高位到低位贪心了。假设现在在考虑第(i)个向量(a_i)(向量降序),其最高位是(x),前(i-1)个向量异或得到的最大整数为(res)
    如果(res)的第xx位是0,则令(res xor a_i),否则不改变(res)。接下来考虑这种做法的正确性。
    如果(res)的第(x)位是0但不改变(res),而改变得到(res′)。由于向量降序,比x更高的位和x这一位无法再改变。即使(res)接下来的位全部是1,也没有(x)这一位是1大,即得到(res<res')。因此操作1正确。
    如果(res)的第(x)位是1但改变(res),得到(res'),由于向量降序,比x更高的位和x这一位无法再改变。即使(res')接下来的位全部是1,也没有x这一位是1大,即得到(res'<res)。因此操作2正确。
    证毕。

    代码:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cctype>
    using namespace std;
    long long p[70], x;
    int n;
    void insert(long long x) {
        for (int i = 60; i >= 0; --i) 
            if ((x >> i) & 1) {
                if (!p[i]) {
                    p[i] = x; break;
                }
                x ^= p[i];
            }
    }
    int main() {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> x, insert(x);
        }
        long long ans = 0;
        for (int i = 60; i >= 0; --i)
            if ((ans ^ p[i]) > ans) ans ^= p[i];
        cout << ans;
        return 0;
    }
    
  • 相关阅读:
    bzoj4128 Matrix 矩阵 BSGS
    bzoj4002 [JLOI2015]有意义的字符串 特征根+矩阵快速幂
    bzoj2476 战场的数目 矩阵快速幂
    bzoj2306 [Ctsc2011]幸福路径 倍增 Floyd
    bzoj2085 [Poi2010]Hamsters 矩阵快速幂+字符串hash
    bzoj1875 [SDOI2009]HH去散步 矩阵快速幂
    bzoj1706 [usaco2007 Nov]relays 奶牛接力跑 矩阵快速幂
    什么是P问题、NP问题和NPC问题[转]
    ExFenwickTree
    CF 816 E. Karen and Supermarket
  • 原文地址:https://www.cnblogs.com/TY02/p/12232383.html
Copyright © 2011-2022 走看看