• 线性基学习笔记


    定义

    张成

    我们定义集合(S subseteq mathbb{N^*}). (T)(S)的任意子集, 所有(T)的异或和组成的集合称为(S)的张成, 写作(span(S)).

    线性相关与线性无关

    假如集合(S)中存在这样一个元素, 我们将其从(S)中去掉后得到的集合为(S')(span(S') = span(S)), 那么我们称集合(S)是线性相关的; 假如不存在这样的一个元素, 则称集合(S)线性无关.

    线性基

    我们称线性无关的集合(B)是集合(S)的线性基, 当且仅当(S subseteq span(B)), 且对于(B)的任意真子集(B')都不满足(s subseteq span(B')).
    (B)中的元素个数称为线性基的长度.
    性质: (S)中的任意元素都可以唯一地表示为(B)中若干个元素的异或和.
    证明:
    反证法. 假设我们可以通过两种方式得到(S)中的某个元素, 也就是说

    [oplus_{k = 1}^n p_k = oplus_{k = 1}^m q_k, p_k, q_k in B ]

    那么就有

    [(oplus_{k = 1}^n p_k) oplus (oplus_{k = 2}^m q_k) = q_1 ]

    显然不满足定义中线性无关的性质, 不成立. 因而命题得证.

    线性基的构造

    鉴于当前没有太多关于线性基的论文可以参考, 下文中可能有许多不严谨之处.
    首先考虑线性基应该是什么样子的: 这与我们构造线性基的目的有关. 线性基通常被用于解决最大异或和/k大异或和问题, 因此它一般会被构建成这样:
    我们设(S)中最大元素在二进制下有(L)位, 则我们用一个编号为([0 ... L - 1])的数组来存储(S)的线性基. 这个数组的含义可以被理解作一种独立和捆绑的关系, 表示哪些位上的(0)(1)被捆绑在一起出现, 哪些位上的(0)(1)互不影响.
    具体来说, 数组上某一位的数假如为(0), 则这一位不能通过异或得到, 或者是这一位与某一更高位捆绑出现; 而假如某一位的数不为(0), 则这一位与更高位相互独立; 同时, 根据这个数的二进制表示, 这一位可能与更低位存在捆绑关系. 不难看出, 一个数位只能存在捆绑和独立中的一种状态, 因此二进制中每一位只能在线性基中出现一次. 比方说, 我们假设这个数组第(2)位上的数为(5), 因为(5)不为零, 因此说明这一位相对于更高位独立存在; 与此同时, 由于(5)的二进制表示为(101), 因此第(2)位的数字与更低位的第(0)位存在捆绑关系.
    明确了线性基的形态, 构建方法就变得显而易见了. 当我们要往线性基中插入一个数(x)时:

    • 在线性基中从高位到低位逐位比较. 我们令当前位为(p), 假如(x)的第(p)位为(1), 则进入下列讨论:
      • 假如线性基的第(p)位不为(0), 则(x)异或上线性基中这一位的数. 原因: (x)通过与线性基中原有的数进行异或, 可以使得当前位和后面的位脱离捆绑关系.
      • 假如线性基的第(p)位为(0), 则我们从第(0)位到第(p - 1)位进行枚举, 假如线性基中的这一位不为(0)并且(x)的这一位也不为(0), 则(x)要异或上线性基中的这个数. 原因: 一个位不可能同时存在捆绑关系与独立关系, 因此我们要消去第(p)位与这一位的捆绑关系. 我们再从第(p + 1)位到最高位进行枚举, 假如线性基中这一位的数中, 与(x)的最高位存在捆绑关系, 则把这个数异或上(x). 原因: 还是一样的.

    代码

    最大异或和

    #include <cstdio>
    #include <cctype>
    
    namespace Zeonfai
    {
        inline long long getInt()
        {
            long long a = 0, sgn = 1;
            char c;
            while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
            while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
            return a * sgn;
        }
    }
    struct linearBasis
    {
        long long a[51];
        inline void insert(long long x)
        {
            for(int i = 50; ~ i; -- i)
                if(x >> i & 1)
                {
                    if(! a[i])
                    {
                        for(int j = 0; j < i; ++ j) if(a[j] && x >> j & 1) x ^= a[j];;
                        a[i] = x;
                        for(int j = 50; j > i; -- j) if(a[j] >> i & 1) a[j] ^= x;
                        break;
                    }
                    else x ^= a[i];
                }
        }
        inline long long getAnswer()
        {
            long long res = 0;
            for(int i = 50; ~ i; -- i) res ^= a[i];
            return res;
        }
    }LB;
    int main()
    {
    
        #ifndef ONLINE_JUDGE
    
        freopen("linearBasis.in", "r", stdin);
        freopen("linearBasis.out", "w", stdout);
    
        #endif
    
        using namespace Zeonfai;
        int n = getInt();
        for(int i = 0; i < n; ++ i) LB.insert(getInt());
        printf("%lld", LB.getAnswer());
    }
    

    第K小异或和

    #include <cstdio>
    #include <cctype>
    
    namespace Zeonfai
    {
        inline long long getInt()
        {
            long long a = 0, sgn = 1;
            char c;
            while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
            while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
            return a * sgn;
        }
    }
    int flg = 0;
    struct linearBasis
    {
        long long a[51];
        inline void insert(long long x)
        {
            for(int i = 50; ~ i; -- i)
                if(x >> i & 1)
                {
                    if(! a[i])
                    {
                        for(int j = i - 1; ~ j; -- j) if(a[j] && x >> j & 1) x ^= a[j];;
                        a[i] = x;
                        for(int j = 50; j > i; -- j) if(a[j] >> i & 1) a[j] ^= x;
                        return;
                    }
                    else x ^= a[i];
                }
            flg = 1;
        }
    }LB;
    int main()
    {
    
        #ifndef ONLINE_JUDGE
    
        freopen("linearBasis.in", "r", stdin);
        freopen("linearBasis.out", "w", stdout);
    
        #endif
    
        using namespace Zeonfai;
        int n = getInt();
        for(int i = 0; i < n; ++ i) LB.insert(getInt());
        int ext[50], cnt = 0;
        for(int i = 0; i <= 50; ++ i) if(LB.a[i]) ext[cnt ++] = i;
        long long sum = (long long)1 << cnt;
        int m = getInt();
        for(int i = 0; i < m; ++ i)
        {
            long long k = getInt() - flg;
            if(k >= sum) {puts("-1"); continue;}
            long long res = 0;
            for(int j = 0; j < cnt; ++ j) if(k >> j & 1) res ^= LB.a[ext[j]];
            printf("%lld
    ", res);
        }
    }
    
  • 相关阅读:
    项目编译
    sqlserver查列名
    list<>初始化赋值两种方式
    看到一句很不错的话
    typescript
    vscode里div等html标签代码补全
    JavaScript 基于原型链的继承
    大数据系列01:大数据离线计算平台hadoop集群搭建和本地环境配置实践
    java数据类型
    计算机基础及java基础
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7323367.html
Copyright © 2020-2023  润新知