本文借鉴oi-wiki
引入
首先学一个算法,要明白这个算法解决什么的问题
线性基是向量空间的一组基,通常可以解决有关异或的一些题目。
性质
- 线性基的元素能相互异或得到原集合的元素的所有相互异或得到的值。
- 线性基是满足性质 1 的最小的集合。
- 线性基没有异或和为 0 的子集。
- 线性基中每个元素的异或方案唯一,也就是说,线性基中不同的异或组合异或出的数都是不一样的。
- 线性基中每个元素的二进制最高位互不相同。
构造
对原集合的每个(x)转为二进制,从高位向低位扫,对于第(i)位是1的,如果(p[i])不存在,那么令(p[i]=x)并结束扫描,如果存在,令(x=xigoplus p[i])
inline void ins(ll x) {
for(int i = 55;i>=0; i--) {
if (!(x &(1ll<<i))) continue;
// x的第i位是0
if(!p[i]){
p[i] = x;
break;
}
x^= p[i];
}
}
问题
异或最大值
将线性基从高位向低位扫,若(igoplus)上当前扫到的(p_i)答案变大,就把答案异或上 。
为什么能行呢?因为从高往低位扫,若当前扫到第(i)位,意味着可以保证答案的第(i)位为 1,且后面没有机会改变第(i)位。
异或最小值
查询原集合内任意几个元素(igoplus)的最小值,就是线性基集合所有元素中最小的那个。
是否可以被异或
查询某个数是否能被异或出来,类似于插入,如果最后插入的数(x)被异或成了 0,则能被异或出来。
个人想法
其实我觉得这个线性基就是把很多个值代替为几十个值,且保证这些值的最高位1都两两不同
相当于变成几十个线性无关组
那么是否可以被异或构成就容易理解了
因为你假如自己可以插入一个值,最后异或值不为0
那么就多了一个线性无关组,这样自己必不能被原来的数表示
模板题
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=2e5+5,mod=1e7;
int n;
ll a[maxn];
ll p[60];
inline void ins(ll x) {
for(int i = 55;i>=0; i--) {
if (!(x &(1ll<<i))) continue;
// x的第i位是0
if(!p[i]){
p[i] = x;
break;
}
x^= p[i];
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
ins(a[i]);
}
ll ans=0;
for(int i=55;i>=0;i--){
if(ans<(ans^p[i]))
ans^=p[i];
}
printf("%lld
",ans);
return 0;
}