zoukankan      html  css  js  c++  java
  • 线性基 (Linear Basis)

    太久没怎么写线性基了,最近多校特别多线性基的题,所以写篇博客权当复习。

    内容主要参考Menci关于线性基博客,menci tql


     线性基是用于解决子集异或一类题目的算法。

    一些定义

    • 异或和:设S为整数集,S的异或和为S中所有元素异或所得的结果。
    • 张成:设T为S一子集,所有S的子集T的异或和组成的集合称为集合S的张成,记作span(S)。
    • 线性相关:对于一个集合S,若存在S的某个元素Si,使得S去除这个元素后得到的集合S'的张成span(S')中包含Si,则称集合S线性相关;或者说,存在这样的元素Si,可以用其他若干个元素异或得到。不满足线性相关的集合成为线性无关的。
    • 线性基:若集合B满足:1、S是B的张成的子集; 2、B是线性无关的。则称集合B为集合S的线性基。集合B中元素的个数称为线性基的长度。

    线性基的基本性质

    • B是极小的满足线性基性质的集合,它的任何真子集都不可能是线性基。
    • S中的任意元素都可以唯一表示为B中若干个元素异或起来的结果。

    线性基的构造方法

    不妨设集合S中最大的数的二进制表示有L位,用一个数组a[0..L]来存储线性基。这种线性基的构造方法保证了一个特殊性质,对于每一个i,ai有以下两种可能:

    1. ai=0,且只有满足j>i的aj(即位于ai后面的所有aj)的第i个二进制位可能为1。
    2. ai≠0,且:整个a数组中只有ai的第i个二进制位为1;ai更高的二进制位一定为0;ai更低的二进制位可能为1。

    我们称第i位存在于线性基中,当且仅当ai≠0。

    线性基是动态构造的,只需从空的a开始,每次考虑在一个已存在的线性基中插入一个数t即可。

    从t最高位上的1开始考虑,设这是第i位。如果这一位已经存在于线性基中,则我们需要将t中的这一位消掉才可以继续插入(因为只有ai的第i位可以为1)。如果这意味不存在于线性基中,则可以将t插入到ai的位置上,但插入时需要保证:

    1. t中比第i位更高的已经存在于线性基中的二进制位必须是0,而这时候t的最高位上的1一定是第i位。
    2. t中比第i位更低的已经存在于线性基中的二进制位必须是0,对于这一点,我们可以枚举t中为1的这些二进制位k对应的元素ak,并用类似上面的方式将t异或ak以消掉这些位上的1。
    3. a中必须只有ai的第i位为1,对于这一点,我们可以枚举ai后面的元素ak(i<k<=L),将每个第i位为1的ak异或上t。

    插入过程

    逆序枚举t的所有为1的二进制位i,对于每个i:

    • 如果ai≠0,则令t=t^ai
    • 如果ai=0,则:
      • 枚举k(0<=k<i),如果t的第k位为1,则令t=t^ak
      • 枚举k(j<k<=L),如果ak的第j位为1,则令ak=ak^t。
      • 令ai=t,插入过程结束。

    线性基的合并

    两个集合的线性基可以在O(log2t)的时间内进行合并,合并后得到的线性基为两个集合的并的线性基。合并时只需将一个线性基中的所有元素插入到另一个线性基中即可。

    线性基的应用

    最基本的应用就是解决子集最大异或和问题。

    问题:给定一个集合S,求其一子集T,使得T异或和最大,求该异或和。

    solution:首先求出S的线性基B,问题转化为选择B的一个子集。从高到低考虑在线性基中的二进制位i,若第i位存在于线性基中,则考虑到线性基中只有唯一一个元素的第i位为1,所以之前加入到T中的元素的异或和的第i位一定为0,故把这个元素加入到T中一定会使得答案更大。故求出线性基中所有元素的异或和,即为答案。

    模板:

    一、不显式构造出集合B,支持动态插入。

     1 struct LinearBasis
     2 {
     3     long long a[MAXL + 1];
     4 
     5     LinearBasis()
     6     {
     7         std::fill(a, a + MAXL + 1, 0);
     8     }
     9 
    10     LinearBasis(long long *x, int n)
    11     {
    12         build(x, n);
    13     }
    14 
    15     void insert(long long t)
    16     {
    17         for (int j = MAXL; j >= 0; j--)
    18         {
    19             if (!t) return;
    20             if (!(t & (1ll << j))) continue;
    21 
    22             if (a[j]) t ^= a[j];
    23             else
    24             {
    25                 for (int k = 0; k < j; k++) if (t & (1ll << k)) t ^= a[k];
    26                 for (int k = j + 1; k <= MAXL; k++) if (a[k] & (1ll << j)) a[k] ^= t;
    27                 a[j] = t;
    28                 break;
    29             }
    30         }
    31     }
    32 
    33     // 数组 x 表示集合 S,下标范围 [1...n]
    34     void build(long long *x, int n)
    35     {
    36         std::fill(a, a + MAXL + 1, 0);
    37 
    38         for (int i = 1; i <= n; i++)
    39         {
    40             insert(x[i]);
    41         }
    42     }
    43 
    44     long long queryMax()
    45     {
    46         long long res = 0;
    47         for (int i = 0; i <= MAXL; i++) res ^= a[i];
    48         return res;
    49     }
    50 
    51     void mergeFrom(const LinearBasis &other)
    52     {
    53         for (int i = 0; i <= MAXL; i++) insert(other.a[i]);
    54     }
    55 
    56     static LinearBasis merge(const LinearBasis &a, const LinearBasis &b)
    57     {
    58         LinearBasis res = a;
    59         for (int i = 0; i <= MAXL; i++) res.insert(b.a[i]);
    60         return res;
    61     }
    62 };
    Code via. Menci

    二、显式构造出集合B,不支持动态插入。

     1 struct LinearBasis
     2 {
     3     std::vector<long long> v;
     4     int n; // 原有集合 S 的大小
     5 
     6     // 数组 x 表示集合 S,下标范围 [1...n]
     7     void build(long long *x, int n)
     8     {
     9         this->n = n;
    10         std::vector<long long> a(MAXL + 1);
    11 
    12         for (int i = 1; i <= n; i++)
    13         {
    14             long long t = x[i];
    15 
    16             for (int j = MAXL; j >= 0; j--)
    17             {
    18                 if ((t & (1ll << j)) == 0) continue;
    19 
    20                 if (a[j]) t ^= a[j];
    21                 else
    22                 {
    23                     for (int k = 0; k < j; k++) if (t & (1ll << k)) t ^= a[k];
    24                     for (int k = j + 1; k <= MAXL; k++) if (a[k] & (1ll << j)) a[k] ^= t;
    25 
    26                     a[j] = t;
    27                     break;
    28                 }
    29             }
    30         }
    31 
    32         v.clear();
    33         for (int i = 0; i <= MAXL; i++) if (a[i]) v.push_back(a[i]);
    34     }
    35 
    36     long long queryMax()
    37     {
    38         long long x = 0;
    39         for (size_t i = 0; i < v.size(); i++) x ^= v[i];
    40         return x;
    41     }
    42 };
    Code via. Menci

    相关题目

    HDU3949

    BZOJ2115

    BZOJ2844

    BZOJ4568

  • 相关阅读:
    OC_框架学习第一天
    OC_协议与分类的学习
    OC内存分析之retain与copy的简单测试示例
    OC类的使用,属性声明与复合类的实现示例
    PL/SQL破解方法(不需要注册码)
    C# Winform应用程序占用内存较大解决方法整理
    分享一个控制台版本《推箱子》小游戏,感兴趣的可以看看
    每天定时去女友空间留言工具(首选你要有个女友。!哈哈哈哈)
    单例模式(Singleton)详解——转载
    easyUI的datagrid控件日期列不能正确显示Json格式数据的解决方案
  • 原文地址:https://www.cnblogs.com/JHSeng/p/11267050.html
Copyright © 2011-2022 走看看