将每个整数看成是一个向量,这些整数的线性基是这些整数的极大异或无关组,每个整数都能唯一的通过线性基内的数异或出来,整数直接互相异或的结果也能通过线性基异或出来,线性基的所有异或结果能通过这些整数异或出来.
构造
线性基是一个一维数组,下标表示二进制位,每个二进制位对应一个线性基的整数向量.
构造时将每个整数逐个插入线性基.设插入值为x.
从高位到低位检查x,为0的位直接跳过,否则检查线性基该位是否已有整数向量,若有则将x异或该向量并跳过该位,否则有两种处理方法.
1.构造阶梯型:直接将x赋给线性基这一位,结束插入操作.
2.构造最简阶梯型:利用低于这一位的整数向量先把x中低于这一位的1消成0,再用x把高于这一位的整数向量中这一位的1消成0
,最后将x赋给线性基的这一位,结束插入操作.
整数子集异或最大值=最简阶梯型线性基所有整数向量异或值
整数子集异或最大值=从高位到低位将能使答案不断变大的上三角线性基中的整数向量异或值异或起来
整数子集异或最小值=最简阶梯型线性基最小的整数向量
某个整数x能被这些整数异或出来=不能插入线性基
从这些整数中选任意个与x异或的最大值=从高到低位将能使答案不断变大的最简阶梯型线性基中的整数向量与x异或
#include<iostream>//构造最简阶梯型线性基
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll lb[64];
void insert(ll x)
{
for(int i=50;i>=0;--i)
{
if(!(x>>i&1))continue;
if(lb[i])
{
x^=lb[i];
continue;
}
for(int j=i-1;j>=0;--j)
if(x>>j&1) x^=lb[j];
for(int j=i+1;j<=50;++j)
if(lb[j]>>i&1) lb[j]^=x;
lb[i]=x;
break;
}
}
int main()
{
// freopen("1.txt","r",stdin);
int n;cin>>n;
for(int i=0;i<n;++i)
{
ll a;scanf("%lld",&a);
insert(a);
}
ll ans=0;
for(int i=0;i<=50;++i)
{
ans^=lb[i];
}
cout<<ans<<endl;
return 0;
}
线性基异或第k小值
k从0开始,计算出线性基后需要将最简阶梯型线性基中的0去掉再从小到大排序,再将k的二进制位为1的对应位置的排序后数组内的整数向量异或起来.
//k从0开始,计算出线性基后需要将线性基中的0去掉再从小到大排序,再将k的二进制位为1的对应位置的排序后数组内的整数向量异或起来.
//本题第k小从1开始
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
ll lb[64];
vector<ll>num;
int add(ll x)
{
for(int i=63;i>=0;--i)
{
if(!(x>>i&1))continue;
if(lb[i]) x^=lb[i];
else
{
for(int j=i-1;j>=0;--j)
{
if(x>>j&1) x^=lb[j];
}
for(int j=i+1;j<=63;++j)
if(lb[j]>>i&1) lb[j]^=x;
lb[i]=x;
return 1;
}
}
return 0;
}
ll Pow(ll x,int y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x;
x=x*x;
y>>=1;
}
return res;
}
int main()
{
// freopen("1.txt","r",stdin);
int t;cin>>t;
int times=1;
while(t--)
{
int n;cin>>n;
memset(lb,0,sizeof(lb));
int f=0;
int s=0;
for(int i=0;i<n;++i)
{
ll a;scanf("%lld",&a);
if(!add(a))
f=1;
else
s++;
}
num.clear();
for(int i=0;i<64;++i)
if(lb[i]) num.push_back(lb[i]);
sort(num.begin(),num.end());
int q;cin>>q;
printf("Case #%d:\n",times++);
while(q--)
{
ll k;scanf("%lld",&k);
if(f)
{
k--;
if(Pow(2,s)<k+1)
{
printf("-1\n");
continue;
}
}
else if(Pow(2,s)-1<k)
{
printf("-1\n");
continue;
}
ll ans=0;
for(int i=0;i<=63&&i<num.size();++i)
{
if(k>>i&1)
ans^=num[i];
}
printf("%lld\n",ans);
}
}
return 0;
}
线段树区间线性基
//查询O(logn*32*32)
const int N=1e4+10;
int tree[N*4][32];
int sl[32];
void add(int x,int lb[])
{
for(int i=31;i>=0;--i)
{
if(!(x>>i&1))continue;
if(lb[i])x^=lb[i];
else
{
lb[i]=x;
return;
}
}
}
void build(int now,int l,int r)
{
if(l==r)
{
int a;scanf("%d",&a);
add(a,tree[now]);
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
for(int i=0;i<32;++i)
{
tree[now][i]=tree[now<<1][i];
add(tree[now<<1|1][i],tree[now]);
}
}
void query(int now,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
for(int i=0;i<32;++i)
add(tree[now][i],sl);
return;
}
int mid=(l+r)>>1;
if(y<=mid)
query(now<<1,l,mid,x,y);
else if(x>mid)
query(now<<1|1,mid+1,r,x,y);
else
{
query(now<<1,l,mid,x,y);
query(now<<1|1,mid+1,r,x,y);
}
}
int main()
{
// freopen("1.txt","r",stdin);
int t;cin>>t;
while(t--)
{
memset(tree,0,sizeof(tree));
int n,q;scanf("%d%d",&n,&q);
build(1,1,n);
while(q--)
{
int l,r;scanf("%d%d",&l,&r);
for(int i=0;i<32;++i) sl[i]=0;
query(1,1,n,l,r);
int ans=0;
for(int i=31;i>=0;--i)
ans=max(ans,ans^sl[i]);
printf("%d\n",ans);
}
}
return 0;
}