线性基学习

  • 什么是线性基
    假设有一个数组A,有m个元素(m可以很大),数组元素 a [ i ] ϵ [ 1 , 2 n − 1 ] a[i]\epsilon[1,2^{n}-1] a[i]ϵ[1,2n1]
    我们设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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值