题解:P3812 【模板】线性基
线性基的定义
称线性空间
异或线性基,换句话说,就是最大的异或和不为
线性空间
本题目要求我们求异或线性基。那么我们接下来介绍一下异或线性基的求法。
高斯消元法
过程
前置知识:高斯消元。
高斯消元法即把每个数化为二进制表示,然后用高斯消元法消成线性基。
例如:对于序列
我们先把这四个数分别转化为二进制矩阵。
然后对这个矩阵进行高斯消元。
消元步骤如下:
- 从高到低位枚举。由于要通过二进制算数,我们尽量把位数从
0 开始从右往左以此递增。 - 确定主元。假设枚举到了第
i 位,我们需要找到一个该位为1 的数。 - 对其他的数:假如这个数第
i 位为1 ,那就让这个数异或主元,让这一位变成0 。
例如,上面的矩阵。
第一步,枚举位数
其他元素在这一位上全部为
接下来枚举
其中红色为主元,蓝色为已确定。
然后开始异或,元素
其中紫色的元素被改变。
然后:枚举
现在我们就得到了一个线性基了。
优点
高斯消元后的矩阵是一个行简化阶梯形矩阵。这意味着,给定一些数,选其中一些异或起来,求异或最大值,使用高斯消元法构造线性基后直接将线性基中所有元素都异或起来输出即可。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
long long a[105],n,p[105],k;
const int bit=51;
void insert(){
for(int i=bit;i>=0;i--){
for(int j=k;j<n;j++) // 把当前第 i 位是 1 的数换上去
if((p[j]>>i)&1){swap(p[j],p[k]); break;}
if(((p[k]>>i)&1)==0) continue; // 当前第 i 位所有数都是 0
for(int j=0;j<n;j++) // 把其他数的第 i 位全部消为 0
if(j!=k&&((p[j]>>i)&1)) p[j]^=p[k]; // 基的个数 +1
k++;
if(k==n) break;
}
}
long long ans=0;
signed main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
p[i]=a[i];
}
insert();
for(int i=k;i>=0;i--){
ans^=p[i];
}
cout<<ans;
return 0;
}
复杂度分析
insert 函数的复杂度是
贪心法
过程
每读到一个数,就插入进来,插入的时候按照以下步骤:
- 从高往低枚举。
- 如果这一位是
1 ,那么看这一位线性基上是否有数。如果有,就异或这个数,然后接着寻找;反之,则直接把线性基的这个位置赋值为这个数。
我们来个例子。还是
首先,
然后插入
然后插入
最后的
得到的线性基是
贪心法求最大值时,需要对线性基求前缀最大值,具体来说,枚举到第
优点
- 贪心法码量略小(也没小多少)。
- 贪心法可以按顺序增添线性基。
代码
#include<bits/stdc++.h>
using namespace std;
long long a[105],n,p[105];
const int bit=63;
void insert(long long now){
for(int i=bit;i>=0;i--){
if(!(now>>(long long)i))continue;
if(!p[i]){
p[i]=now;
break;
}
now^=p[i];
}
}
long long ans=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
insert(a[i]);
}
for(int i=bit;i>=0;i--){
if((ans xor p[i])>ans)ans^=p[i];
}
cout<<ans;
return 0;
}
复杂度分析
虽说贪心法一次插入仅为
这就是线性基的两种求法。