zoukankan      html  css  js  c++  java
  • 线性基

    线性基

    基:在线性代数中,基(也称为基底)是描述、刻画向量空间的基本工具。向量空间的基是它的一个特殊的子集,基的元素称为基向量。向量空间中任意一个元素,都可以唯一地表示成基向量的线性组合。如果基中元素个数有限,就称向量空间为有限维向量空间,将元素的个数称作向量空间的维数。
    同样的,线性基是一种特殊的基,它通常会在异或运算中出现,它的意义是:通过原集合S的某一个最小子集S1使得S1内元素相互异或得到的值域与原集合S相互异或得到的值域相同。
    

    以上内容来自某度某科

    简单来说,线性基的异或集合与集合(S)异或集合(我们暂且把一个集合中任选元素相互异或的值域叫做异或集合)完全相同,线性基中的元素可以含有与(S)中的元素不同的元素,但必须可以由(S)中的元素相互异或得到,否则异或集合会不同。

    性质

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

    维护

    插入

    插入元素就是先从二进制的最高位开始枚举,若插入元素的对应的位是1,那么就看线性基这一位是否为空,若为空则将这个元素放到这一位上,然后插入成功,直接结束,若不为空,则用这个元素异或一下线性基这一位的元素,然后继续枚举,直到最后,每一个元素只有两个结果,一是被插入线性基,二是最后被异或为(0),不用管了。直接讲有些抽象,还是上代码吧。
    时间复杂度(O(log))

    void insert(x){
        for(rg int j=29;j>=0;--j)
        	if(x>>j&1){
    	        if(!b[j]){b[j]=s;break;}
    	    	else x^=b[j];//b是线性基
    	    }
    	    if(!x)break;
    }
    

    合并

    合并两个线性基其实就是将其中一个线性基的元素一个一个插入到另一个线性基,得到的新线性基就是合并之后的线性基,也就是启发式合并,这个很好理解,就不贴代码了
    时间复杂度(O(log^2))(一个线性基有(log)个元素,每次插入都是(log)的)

    查询

    线性基最常见的查询就是异或最大值和异或最小值,而这个查询其实就是一个按位贪心的过程,以最大值为例。
    由于线性基中每一个元素的最高位不同,所以我们依旧从最高位枚举,如果到了一位为0位上,那么就将答案异或一下线性基与这一位对应的元素,这样我们保证了高位尽量为1,所以值就是最大(最小值反之,保证高位尽量为0即可)
    时间复杂度(O(log))

    int query(){
        int s=0;
        for(rg int k=29;k>=0;--k)
    	    if(~s>>k&1)s^=b[k];
    	return s;
    }
    

    然后还有一些其他的查询博主就不讲啦,线性基入门到这就差不多了。

    总结

    线性基一般不会单独使用,一般会套一些数据结构,分治算法之类的东西或者一些奇奇怪怪的做法,单独使用效率也许还不如暴力。

  • 相关阅读:
    线程池原理分析
    强引用-软引用-弱引用
    并发编程之多线程
    linux关于获取时间的几个函数
    gdb安装和使用
    c++四种显式类型转换
    ARP协议
    Vmware 共享文件夹不显示的问题
    gdb基本使用
    动态二维数组实现
  • 原文地址:https://www.cnblogs.com/ljq-despair/p/8724222.html
Copyright © 2011-2022 走看看