线性基 (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

原文地址:https://www.cnblogs.com/JHSeng/p/11267050.html