线性基学习

做一下整理:

1、线性基:

  若干数的线性基是一组数p1,p2,...pn,其中px的最高位的1在第x位。

  通过线性基中元素xor出的数的值域与原来的数xor出数的值域相同。

2、性质:

性质

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

3、线性基的构造法:

  对每一个数a从高位到低位扫,扫到第x位为1时,若px不存在,则px=a并结束此数的扫描,否则令a = a   xor px。

void build(int n) {
	for(int i = 1; i <= n; i++) {
		for(int j = 60; j >= 0; j--) {
			if((a[i] >> j) & 1) {
				if(!p[j]) {
					p[j] = a[i];
					break;
				}
				a[i] ^= p[j];
			}
		}
	}
} 

两个线性基合并: 把p2中的线性基,从大到小都加到p1中即可

// p2 加到 p1 上 
void merge() {
	int x;
	for(int i = 60; i >= 0; i--) {
		if(p2[i]) {
			x = p2[i];
			for(int j = 60; j >= 0; j--) {
				if((x >> j) & 1) {
					if(!p[j]) {
						p[j] = x;
						break;
					}
					x ^= p[j];
				}
			}
		}
	}
}

4、查询

查询最大值:从高位开始异或,得到的结果大于当前值就更新

ll querymax() {
	ll ans = 0;
	for(int i = 60; i >= 0; i--)
		if(ans < (ans ^ p[i]))
			ans ^= p[i];
	return ans;
}

查询最小值:存在最低位的数即为最小值

ll querymin() {
	for(int i = 0; i <= 60; i++)
		if(p[i])
			return p[i];
	return 0;
}

查询某个值k是否存在:从高位开始异或,看是否能形成k

bool judge(ll k) {
	for(int i = 60; i >= 0; i--)
		if((k >> i) & 1)
			k = k ^ p[i];
	return k == 0;
}

查询第k小的数:我们要将线性基改造成每一位相互独立,具体操作就是如果i<j,pj的第i位是1,就将pj异或上pi。经过一系列操作之后,对于二进制的某一位i。只有pi的这一位是1,其他都是0。 所以查询的时候将k二进制拆分,对于1的位就异或上对应的线性基。 最终得出的答案就是k小值。

void rebuild() {
	cnt = 0;
	for(int i = 60; i >= 0; i--)
		for(int j = i - 1; j >= 0; j--)
			if((p[i] >> j) & 1)
				p[i] ^= p[j];
	for(int i = 0; i <= 60; i++)
		if(p[i])
			p[cnt++] = p[i];
}
ll querykth(ll k) {
	
	ll ans = 0;
	if(k >= (1LL << cnt)) return -1;
	for(int i = 60; i >= 0; i--)
		if((k >> i) & 1)
			ans ^= p[i];
	return ans;
}

5、应用:看一下这个大佬的博客吧:https://www.cnblogs.com/vb4896/p/6149022.html

应用六:

我就加一个,多次查询区间的,取任意个数的异或和最大,这就是HDU第一场的一个题,这就要用一个二维的记录一下,并且从高到低位保存的是最右边的线性基,并且记录下位置,以便后边查询区间的时候进行判断:

链接:https://blog.csdn.net/mmk27_word/article/details/96898031

 

 

 

学习博客:学习1   学习2   学习3  学习4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值