线性基浅谈

知识串联:

  1.     基:域内的其他值可以用 “基” 线性表示
  2.     线性基:以 异或(xor) 为基础的运算
  3.     二维向量空间的基:最常见的是(1, 0) (0, 1) ;这样所有向量都可以用这两个向量表示!!当然还有其他的基,比如说:(2, 0),(0, 1);(2, 1),(0,2) 等……
  •    举个例子:向量(3, 5)
  •       1.以 a =(1,0)b =(0,1) 为基; (3,5)=3*(1,0)+5*(0,1)=3*a+5*b;
  •       2.以 a =(2,1)b =(0,2)为基;(3,5)=(3/2)*(2,1) + (7/4) * (0,2) = (3/2) * a + (7/4) * b;

步如正题——线性基构造

     问题:怎样构造基,能使其他数仅通过异或(xor)就能构造出来?

     解决方法:类似于二维线性空间,第i位放1,其余放0

        举个例子:4位二进制的基:1000 0100 0010 0001,这样无论什么数都可以被异或出来,比如0101 = (0100) xor (0001)…方法就是第几位有1,就异或(xor)第几个基!

BUT!!!这样做没有必要

    类似于二维向量的基(2, 1)(0,2)一样,我们只需要保证第 i 位不为 0 ,高位都为0,低位无所谓

        举个例子:4位二进制的基:1010 0110 0011 0001,这样无论什么数也可以被异或出来,比如说0101 = (0110) xor (0011)

            方法:就是当前第几位有1,就异或(xor)第几个基,然后用结果继续运算直到为0000。

            举个例子:(0100) xor  (0110) = (0010)

                              (0010) xor (0011) = (0001)

                              (0001) xor(0001) = (0000)

BUT!!!这样做还有个问题 ——如果基是随意构造的话,那就没有了意义

    那么我们在给线性基提一个要求,要求所有的基都是由给定的数构造而来。

    举个例子:由给定的1010 1111 1001 1100 构造线性基

    方法:用第i个数构造基的时候,从前向后扫描,如果这一位有1就异或xor掉,没有就放入 线性基 中,如果异或成0000就不用加入,因为他可以用其他基表示。

    第一个数1010, 直接放入基中(基:1010)

    第二个数1111,与基中的数异或 (1111 xor 1010 = 0101) ,基中没有第2位为1的数,所以将0101放入基中(基:1010 0101)

    第三个数1001,与基中的数异或(1001 xor 1010 = 0011),基中没有第3位为1的数,所以将0011放入基中(基:1010 0101 0011)

    第四个数1100,与基中的数异或(1100 xor 1010 = 0110),但是基中存在第二位为1的数,所以将第二位异或了 (0110 xor 0101 =0011),但是基中也存在,第3位为1的数,所以将第3位异或了(0011 xor 0011 =0000),当结果为0000说明这个数可以由之前的数线性表示(1100 = 1010 xor 0101 xor 0011),如果加入基,不满足基线性无关的原则。

   这样 线性基 中的基就是给定元素或者给定元素异或xor出的值,并且线性无关

代码实现

void Guass() {
    for (int i=1;i<=sz;i++) {
        for (int j=62;j>=0;j--) {
            if ((A[i]>>j)&1) {
                if (!P[j]) {P[j]=A[i]; break;}
                else A[i]^=P[j];
            }
        }
    }
    for (int j=0;j<=62;j++) if (P[j]) r++;
}

应用

应用一:给出一个数组,询问一个数能否表示为数组中的某些数的异或和。

解法: 先求出线性基, 从二进制高位往低位判断,假设当前x的这一位是1,如果线性基中存在某个向量最高位的1在这一位,那么这个向量肯定要取,把x异或这个向量,否则没法组合出来。

理由:基中的元素也是由给定的数异或出来的,尽管不知道是由哪些数异或出来,但是这不重要

BZOJ2460 BZOJ2115

struct Base{
	
	int x[31];
	
	void Insert(int v){
		if (!v) return; 
		for (int i = 30; i>= 0; i--){
			if ((1 << i) & v){
				if (x[i]) v ^= x[i];
				else {
					x[i] = v;
					break;
				}
			}
		}
	}
	
	int Query(int v) {//求给定数与基中数异或的最小值
		for (int i = 30; i >= 0; i--){
			if ((v ^ x[i]) < v) v ^= x[i];
		}
		return v;
	}
	
}base;

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值