- 什么是线性基?
假设有一个数组A,有m个元素(m可以很大),数组元素 a [ i ] ϵ [ 1 , 2 n − 1 ] a[i]\epsilon[1,2^{n}-1] a[i]ϵ[1,2n−1],
我们设A数组里面任意子集的异或所得的不同结果,为集合XOR_A.
对于这样的数组A,我们可以构造一个A的线性基B(也用数组),线性基的大小为n。这线性基里任意子集的异或所得的不同结果,为XOR_B.
除了XOR_B不存在异或为0的结果外,XOR_A和XOR_B可以说是相等的。
结果是相同的,但压缩了空间,除此外线性基还有很多比较方便的性质。(到题目里细讲)
讲的不是很严谨,大意是这样。
更详细可以看这里:线性基模板以及讲解然后开始刷题。
1.BZOJ2460
题意:给n块石头,石头有两个属性标号id和魔法值v,选取任意数量的石头,这些石头的编号子集异或不能为0,并且使魔法值的和最大。
首先线性基里的元素互相异或不等于0。
当一个元素尝试插入线性基时,要出来被异或成0(插入失败),否则就插入成功。
所以这个题我们先插入魔法值v最大的石头。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+100;
struct node{
ll id,v;
}A[N];
bool cmp(node x,node y){
return x.v>y.v;
}
ll B[63];
bool insert(ll x){
for(int i=63;i>=0;i--){
if((x>>i)&1){
if(!B[i]){B[i]=x;break;}
else x^=B[i];
}
}
return x;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>A[i].id>>A[i].v;
sort(A+1,A+1+n,cmp);
ll ans=0;
for(int i=1;i<=n;i++)
if(insert(A[i].id))ans+= A[i].v;
cout<<ans<<endl;
}
2.牛客练习赛49E
求线性基里元素个数的奇偶性就行。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll B[63];
bool insert(ll x){
for(int i=63;i>=0;i--){
if((x>>i)&1){
if(!B[i]){B[i]=x;break;}
else x^=B[i];
}
}
return x;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
ll n,k;
cin>>n;
bool flag=0;
while(n--){
cin>>k;
if(insert(k))flag=!flag;
}
if(flag)cout<<"First\n";
else cout<<"Second\n";
}
3.HDU 3949求异或第k小
思路:线性基模板题,先重建线性基,使重建后的线性基的每个最高位为1,其余为0,就像1,2,4,8,16,32…这样。然后查询第k小,把k化成二进制对应的1位在线性基上进行异或。就可以得到答案。
但要注意线性基里不存在异或为0的结果,所以插入的时候要特判一下是否有为0的结果。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll B[63],C[63],cnt,;
bool insert(ll x){//插入
for(int i=63;i>=0;i--){
if(x>>i&1){ //位移运算优先级更高
if(!B[i]){B[i]=x;break;}
else x^=B[i];
}
}
return x;
}
void rebuild(){//重构B数组
cnt=0;
for(int i=63;i>=0;i--)
for(int j=i-1;j>=0;j--)
if(B[i]>>j&1)
B[i]^=B[j];
for(int i=0;i<=63;i++)
if(B[i])C[cnt++]=B[i];
}
ll query_k(ll k){
ll ret=0;
if(k>=((ll)1<<cnt))
return -1;
for(int i=63;i>=0;i--)
if(k>>i&1)ret^=C[i];
return ret;
}
int main(){
ios::sync_with_stdio(false);
int T,kase=0;
cin>>T;
while(T--){
cout<<"Case #"<<++kase<<":\n";
memset(B,0,sizeof(B));
memset(C,0,sizeof(C));
ll n,q,x; bool flag=0;
cin>>n;
while(n--){
cin>>x;
if(!insert(x))flag=1;
//异或值出现为0的情况就特判
}
rebuild();
cin>>q;
while(q--){
cin>>x;
if(flag&&x==1)cout<<"0\n";
else cout<<query_k(x-flag)<<endl;
}
}
}
最后附上一波代码模板
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll B[63],C[63],cnt;
ll q_max(){//查询最大
ll ret=0;
for(int i=63;i>=0;i--)
ret=max(ret,ret^B[i]);
return ret;
}
ll q_min(){
for(int i=0;i<=63;i++)
if(B[i])return B[i];
return 0;
}
bool insert(ll x){//插入
for(int i=63;i>=0;i--){
if(x>>i&1){ //位移运算优先级更高
if(!B[i]){B[i]=x;break;}
else x^=B[i];
}
}
return x;
}
void merge(){//将线性基C合并入线性基B
for(int i=0;i<=63;i++)
if(C[i])insert(C[i]);
}
void rebuild(){//重构B数组
cnt=0;
for(int i=63;i>=0;i--)
for(int j=i-1;j>=0;j--)
if(B[i]>>j&1)
B[i]^=B[j];
for(int i=0;i<=63;i++)
if(B[i])C[cnt++]=B[i];
}
ll query_k(ll k){//查询第k小
ll ret=0;
if(k>=((ll)1<<cnt))
return -1;
for(int i=63;i>=0;i--)
if(k>>i&1)ret^=C[i];
return ret;
}