【学习小结】线性基

本文介绍了线性基的概念和性质,重点讨论了线性基的合并策略和如何利用线性基进行高效的查询操作,包括求解最小值和k大值。此外,还分享了一些在动态维护线性基时的技巧,如保证高位数优先和时间戳线性基的思想,适用于解决区间抑或最大值问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

menci的讲解
学习了Yveh的博客

发现了线性基的一些性质和用法

线性基合并

直接把另一个暴力插入。
可以判一下如果满了就不插入,剪枝很有作用

一定要满足从高位到低位贪心

struct Data{
	int a[32];
	bool flag;

	Data(){ memset(a,0,sizeof(a)); flag = 0; }
	Data operator + (Data z){
		Data res = z;
		if ( z.flag ) return res;
		if ( flag ) return *this;
		for (int i = 30 ; i >= 0 ; i--){
			int x = a[i];
			for (int j = 30 ; j >= 0 ; j--){
				if ( x >> j & 1 ){
					if ( !(res.a[j] >> j & 1) ){ res.a[j] = x; break; }
					x ^= res.a[j];
				}
			}
		}
		res.flag = 1;
		for (int i = 29 ; i >= 0 ; i--){
			res.flag &= (res.a[i] >> i) & 1;
			if ( res.a[30] >> i & 1 ){
				res.a[30] ^= res.a[i];
			}
		}
		return res;
	}
	void insert(int x){
		for (int j = 29 ; j >= 0 ; j--){
			if ( x >> j & 1 ){
				if ( !(a[j] >> j & 1) ){ a[j] = x; break; }
				x ^= a[j];
			}
		}
	}
};

线性基的性质

如果把线性基定义为所有元素一定是一个线性无关组

有如下性质
1.设线性基的异或集合中不存在00。
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。
3.线性基二进制最高位互不相同。
4.如果线性基是满的,它的异或集合为[1,2n−1][1,2n−1]。
5.线性基中元素互相异或,异或集合不变。

那么可以查询能够组合出来的数的最小值、k大值

k大值查询
线性基要维护成有主元的位,只有主元是1,没有主元的位显然不会影响答案
那么,直接贪心即可。选择第i个主元相当于排名上升2i-1

一些技巧

hdu 6579 , 2019 Multi-University Training Contest 1 - B operation
求区间选一些数的抑或最大值。在后面动态加入,强制在线。要求复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

不需要维护区间线性基,我们只需要贪心的维护,使得线性基高位的数的位置尽量靠右。这样高位更可能被选到
以前更早加入的数被重复的往下替换
相当于是时间戳线性基。(有点像维护连通性的时间最大生成树)

#include<bits/stdc++.h>
using namespace std;

const int maxn = 5e5 + 20;

struct Data{
	int a[32],id[32];
	bool flag;

	Data(){ memset(a,0,sizeof(a)); memset(id,0,sizeof(id)); flag = 0; }
	void insert(int x,int curid){
		for (int j = 29 ; j >= 0 ; j--){
			if ( (x >> j) & 1 ){
				if ( !(a[j] >> j & 1) ) { a[j] = x , id[j] = curid; break; } 
				else if ( id[j] < curid ){
					swap(id[j],curid);
					swap(a[j],x);
				}
				x ^= a[j];
			}
		}
	}
	int query(int curid){
		int res = 0;
		for (int j = 29 ; j >= 0 ; j--){
			if ( !((res >> j) & 1) ){
				if ( id[j] >= curid ) res ^= a[j];
			}
		}
		return res;
	}
}base[maxn];
int n,m,a[maxn];

int main(){
	int cases;
	scanf("%d",&cases);
	while ( cases-- ){
		scanf("%d %d",&n,&m);
		for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
		for (int i = 1 ; i <= n ; i++){
			base[i] = base[i - 1];
			base[i].insert(a[i],i);
		}
		int lastans = 0;
		for (int i = 1 ; i <= m ; i++){
			int tp,l,r,x;
			scanf("%d",&tp);
			if (tp == 0 ){
				scanf("%d %d",&l,&r);
				l = (l ^ lastans) % n + 1 , r = (r ^ lastans) % n + 1;
				if ( l > r ) swap(l,r);
				lastans = base[r].query(l);
				printf("%d\n",lastans);
			}else{
				scanf("%d",&x);
				x = (x ^ lastans);
				n++;
				base[n] = base[n - 1];
				base[n].insert(x,n);
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值