常用异或算法:线性基

正文

线性基基础理论

1.0 线性基是啥?

线性基是一个数的集合,并且每个序列都拥有至少一个线性基,取线性基中若干个数异或起来可以得到原序列中的任何一个数。

2.0 线性基三大性质

  • 原序列里面的任意一个数都可以由线性基里面的一些数异或得到
  • 线性基里面的任意一些数异或起来都不能得到 0
  • 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

3.0 线性基的构造

那么它是怎么构造的呢?

设数组 d dd 表示序列 a aa 的线性基,下标从 0 00 开始算。(为了方便理解,我们设 x ( 2 ) 为 x的二进制数

void insert(ll x)
{
    for(int i=60;i>=0;i--)
    {
        if(x&(1ll<<i))//注意,如果i大于31,前面的1的后面一定要加ll
        {
            if(d[i])x^=d[i];
            else
            {
                d[i]=x;
                break;//插入成功就退出
            }
        }
    }
}

据此,我们可以得到一个关于d数组的性质:若 d [ i ] 不为0,则 d[i] (2) 的第 i + 1 i+1i+1 位一定为 1,并且 d [ i ] (2) 的最高位就是第 i + 1位。

个人理解,就是把一个数分解成二进制,然后是进行不断异或,如果变为0,证明已经可以由线性基中的其他数异或得到,否则可以成功插入

线性基性质证明

1.0 证明性质1

我们知道了线性基的构造方法后,其实就可以很容易想到如何证明性质 1 11 了,我们设原序列里面有一个数 x xx,我们尝试用它来构造线性基,那么会有两种结果——1、不能成功插入线性基;2、成功插入线性基。
在这里插入图片描述

2.0 证明性质2

在这里插入图片描述

3.0 证明性质3

在这里插入图片描述

线性基性质应用

如何求最大值?

如何求在一个序列中,取若干个数,使得它们的异或和最大?

首先构造出这个序列的线性基,然后从线性基的最高位开始,假如当前的答案异或线性基的这个元素可以变得更大,那么就异或它,答案的初值为 0 00。
代码如下:

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

为啥求解是个贪心的过程?

前面说过,d[i] (2)的第i + 1位一定为1,联想一下这里,无非就是两种情况:

  • ans(2)的第 i + 1 i+1i+1位为 0
    对于这种情况,ans 异或了 d[i] 之后一定会变大,那么我们就选择异或它。
    可能有人会问,ans 异或完d[i] 后虽然i+1位变成了1,但是后面的 1 ~ i 位可能会受到影响啊。无论后面怎么变,就算后面的 1 11 ~ i ii 位都能变成1,贡献也没有第 i + 1 i+1i+1 位变成1的贡献大呀。
  • ans(2)的第 i + 1位为 1
    如果 a n s ansans 异或了 d [ i ],那么第 i+1 位就会变成 0,后面怎么异或也补救不了第i+1 位变成 0 的损失了,按照上面的思想,要优先使最高位尽可能大,所以此时,ans 不能异或 d[i]。

如何求最小值?

注意,这里指的是用线性基内的元素能异或出的最小值。

显然就是最小的 d [ i ] 了,因为最小的 d [ i ]无论异或谁都会变大。

如果是求整个序列能异或出的最小值而不是这个序列的线性基能异或出的最小值的话,要另外看一看有没有元素不能插入线性基,如果有,那么最小值就是 0,否则依然是最小的d[i]。

long long query_min()
{
    for (int i=0;i<=60;i++)
        if (d[i])
            return d[i];
    return 0;
}

如何求第k小的值 ?

在这里插入图片描述

void work()//处理线性基
{
	for(int i=1;i<=60;i++)
	for(int j=1;j<=i;j++)
	if(d[i]&(1ll<<(j-1)))d[i]^=d[j-1];
}

if(d[i]&(1ll<<(j-1)))d[i]^=d[j-1];,d[i]的最高位在i+1,所以是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;
	}
}

对线性基进行处理,使得可以通过二进制进行判别大小!!!

结语


“遇事不决可问春风,春风不语即随本心”的意思是:对一件事犹豫不决,就问春风该如何做,春风给不出答案,就凭自己本心做出决断。“遇事不决可问春风,春风不语即随本心”一句出自网络作家“烽火戏诸侯”的《剑来》,其原文是:“遇事不决,可问春风。春风不语,遵循己心”。

在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/weixin_46627433/article/details/123800475
今日推荐