线性基系统学习

学习博客:https://blog.csdn.net/a_forever_dream/article/details/83654397

首先说说线性基是什么:

线性基,顾名思义,是一种线性的数,是一个序列按照某种方式处理之后得到的产物,并且有如下性质:

1.原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
2.线性基里面的任意一些数异或起来都不能得到
3.线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的
下面看一下线性基的求法:

for(int i=34;i>=0;i--)
        {
            if(x&(1ll<<i))//这一位为1
            {
                if(!basis[i])//还没有存过这一位
                {
                    basis[i]=x;break;
                }
                x^=basis[i];
            }
        }

下面看一下线性基的一些常用的用法:

求最大值:求一个序列中,取若干个数,使得他们的异或和最大

首先构造出这个序列的线性基,然后从线性基的最高位开始,贪心的方法得到最大值

代码:

ll ans()
{
    ll anss=0;
    for(int i=50;i>=0;i--)//记得从线性基的最高位开始
    if((anss^d[i])>anss)anss^=d[i];
    return anss;
 }   

求最小值:最小值显然线性基中就是最小的一个元素,这就不用说了吧

求第K小的值:求一个序列中,取若干个数,使得他们的异或值在所有异或出来的数中第K小

首先要对线性基进行处理,对于每一个d[i],枚举j=i~1,如果d[i](2)的第j位为1,那么d[i]异或d[j]

最终得到的线性基大概是这样的:

(x表示0或1):
       1xxxx0xxx0x
                1xxx0x
                        1x

看代码:

void work()//处理线性基
{
    for(int i=1;i<=60;i++)
    for(int j=1;j<=i;j++)
    if(d[i]&(1<<(j-1)))d[i]^=d[j-1];
}
ll k_th(ll k)
{
    if(k==1&&tot<n)return 0;//特判一下,假如k=1,并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
    if(tot<n)k--;//类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
    work();
    ll ans=0;
    for(int i=0;i<=60;i++)
    if(d[i]!=0)
    {
        if(k%2==1)ans^=d[i];
        k/=2;
    }
}

如何判断一个数是否能被当前线性基中的元素异或得到

       把它尝试插入进线性基里面去,假如可以插入,说明不能异或得到,假如插不进去,说明不能异或得到 (原理然而上面已经讲了)

当初的梦想实现了吗,事到如今只好放弃吗~
原文地址:https://www.cnblogs.com/caijiaming/p/11260787.html