线性基汇总

写在前面

因为时间原因,就不写学习笔记了(其实是因为懒)。只是简单地把今天上午到现在刷的题汇总一下。
如果有想学习知识的同志还是建议先看一下这篇博客—>线性基详解

三条性质

  • 线性基的核心有三条性质,蒟蒻今天刷的题也基本上是这三条性质的应用

性质一:原序列里面的任意一个数都可以由线性基里面的一些数异或得到

主要应用:[WC2011]最大XOR和路径
题解戳这里


性质二:线性基里面的任意一些数异或起来都不能得到 (0)

主要应用:[BJWC2011]元素

  • 贪心思想,排序优先选大的,用线性基判断能不能选就行了
(Code)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1010
#define ll long long
#define R register
using namespace std;
inline ll read(){
	ll x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n;
ll p[65],ans;
struct data{
	ll num;
	int mag;
	inline bool operator <(const data &a)const{
		return mag > a.mag;
	}
}a[N];
bool check(ll x){
	for(R int i = 62;i>=0;i--){
		if(!(x>>i))continue;
		if(!p[i]){
			p[i] = x;
			return 1;//说明可以被选
		}
		x ^= p[i];
	}
	return 0;//被消完了就不能被选
}
int main(){
	n = read();
	for(R int i = 1;i <= n;i++){
		a[i].num = read(),a[i].mag = read();
	}
	sort(a+1,a+1+n);//贪心,先选大的
	for(R int i = 1;i <= n;i++)	{
		if(check(a[i].num))ans+=a[i].mag;
	}
	printf("%lld
",ans);
	return 0;
}

性质三:线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

主要应用:[TJOI2008]彩灯

  • 这题其实也用到了性质一,放在这里是因为只有符合性质三的同时才可以将答案用线性基统计出来
  • 显然开关相当于异或操作,讲每一串灯控制的范围转化为 (10) 进制数,放进线性基里,此时线性基内的元素便可表示所有情况,每个元素都有选或不选的情况。故有 (2^{cnt}) 种情况,(cnt) 即为线性基内元素的个数,插入的时候统计下来就行了。
(Code)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 20010
#define ll long long
#define R register
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int mod = 2008;
int n,m;
ll p[100],cnt;
char s[100];
void LineBase(ll x){
	for(R ll i = 55;i>=0;i--){
		if(!(x>>i))continue;
		if(!p[i]){
			p[i] = x;
			cnt++;
			break;
		}
		x ^= p[i];
	}
}
int main(){
	n = read(),m = read();
	for(R ll i = 1;i <= m;i++){
		scanf("%s",s+1);
		ll len = strlen(s+1);
		ll tmp = 0;
		for(R ll j = 1;j <= len;j++){
			tmp += (s[j]=='O') ? 1ll<<(j-1) : 0;
		}
		LineBase(tmp);
	}
	ll ans = pow(2ll,cnt);
	printf("%lld
",ans%mod);
	return 0;
}

还有这道题也是很不错的一道题,是用倍增将线性基快速合并
[SCOI2016]幸运数字
题解戳这里

原文地址:https://www.cnblogs.com/hhhhalo/p/13858504.html