题解 P3812 【【模板】线性基】
线性基简介
线性基是一种擅长处理异或问题的数据结构.设值域为
一个线性基满足,对于它所表示的所有数的集合
插入和判断
我们考虑插入的操作,令插入的数为
- 若线性基的第
i 位为0 ,则直接在该位插入x ,退出; - 若线性基的第
i 位已经有值a_i ,则x = x\oplus a_i ,重复以上操作直到x=0 。
如果退出时
很容易证明这样复杂度为
void ins(ll x){
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i]){a[i]=x;return;}
else x^=a[i];
flag=true;
}
bool check(ll x){
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i])return false;
else x^=a[i];
return true;
}
查询异或最值
查询最小值相对比较简单。考虑插入的过程,因为每一次跳转操作,
考虑异或最大值,从高到低遍历线性基,考虑到第
同样,我们考虑对于一个数
查询k 小值
我们考虑进一步简化线性基。显然,一个线性基肯定可以表示为若干个形如
经过这一步操作后,设线性基内共有
同样,考虑最小值时,也必须要考虑到
随后,我们考虑将
学过线性代数的同学应该可以看出,这个过程就是对一个矩阵求解异或意义下的秩的过程。因此,
同样,有关线性基的一切运算都可以看做矩阵的初等行列变换,也就可以将其看做线性规划问题。同样,可以离线使用高斯消元来构造极小线性基。
bool flag;//可以表示0
ll qmax(ll res=0){
for(reg int i=MN;~i;i--)
res=max(res,res^a[i]);
return res;
}
ll qmin(ll res=0){
if(flag)return 0;
for(reg int i=0;i<=MN;i++)
if(a[i])return a[i];
}
ll query(ll k){
reg ll res=0;reg int cnt=0;
k-=flag;if(!k)return 0;
for(reg int i=0;i<=MN;i++){
for(int j=i-1;~j;j--)
if(a[i]&(1ll<<j))a[i]^=a[j];
if(a[i])tmp[cnt++]=a[i];
}
if(k>=(1ll<<cnt))return -1;
for(reg int i=0;i<cnt;i++)
if(k&(1ll<<i))res^=tmp[i];
return res;
}
代码
#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int MN=60;
ll a[61],tmp[61];
bool flag;
void ins(ll x){
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i]){a[i]=x;return;}
else x^=a[i];
flag=true;
}
bool check(ll x){
for(reg int i=MN;~i;i--)
if(x&(1ll<<i))
if(!a[i])return false;
else x^=a[i];
return true;
}
ll qmax(ll res=0){
for(reg int i=MN;~i;i--)
res=max(res,res^a[i]);
return res;
}
ll qmin(){
if(flag)return 0;
for(reg int i=0;i<=MN;i++)
if(a[i])return a[i];
}
ll query(ll k){
reg ll res=0;reg int cnt=0;
k-=flag;if(!k)return 0;
for(reg int i=0;i<=MN;i++){
for(int j=i-1;~j;j--)
if(a[i]&(1ll<<j))a[i]^=a[j];
if(a[i])tmp[cnt++]=a[i];
}
if(k>=(1ll<<cnt))return -1;
for(reg int i=0;i<cnt;i++)
if(k&(1ll<<i))res^=tmp[i];
return res;
}
int main(){
int n;ll x;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&x),ins(x);
printf("%lld\n",qmax());
return 0;
}