定义
张成
我们定义集合(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)中的某个元素, 也就是说
那么就有
显然不满足定义中线性无关的性质, 不成立. 因而命题得证.
线性基的构造
鉴于当前没有太多关于线性基的论文可以参考, 下文中可能有许多不严谨之处.
首先考虑线性基应该是什么样子的: 这与我们构造线性基的目的有关. 线性基通常被用于解决最大异或和/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);
}
}