0
点赞
收藏
分享

微信扫一扫

关于线性基的学习与理解 性质

爱情锦囊 2022-03-13 阅读 34

此处只拿出他们做题总结的部分,原理过程删掉了

1、线性基:

  若干数的线性基是一组数a1,a2,...an,其中ax的最高位的1在第x位。

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

2、线性基的构造法:

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

3、查询:

  用线性基求这组数xor出的最大值:从高往低扫ax,若异或上ax使答案变大,则异或。

4、判断:

  用线性基求一个数能否被xor出:从高到低,对该数每个是1的位置x,将这个数异或上axax(注意异或后这个数为1的位置和原数就不一样了),若最终变为0,则可被异或出。当然需要特判00(在构造过程中看是否有p变为0即可)。例子:(11111,10001)(11111,10001)的线性基是a5=11111,a4=01110,要判断11111能否被xor出,11111 xora5=0,则这个数后来就没有是1的位置了,最终得到结果为00,说明11111能被xor出。


关于线性基的模板:

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N],p[100];
int n;
int insert(ll x){

for(int j=62;j>=0;j--) {
if(!(x>>j&1)) continue;
if(!p[j]) { p[j]=x; return 1; }
x^=p[j];
}
return 0;
}

一些性质:

你可以理解为将一个序列处理完之后得到的产物,并且有如下性质(后面有证明):

1、原序列里面的任意一个数都可以由线性基里面的一些数异或得到。

2、线性基里面的任意一些数异或起来都不能得到0 00

3、线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的


证明性质1

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

分类讨论一下


1、不能成功插入线性基**

       什么时候不能插入进去呢?

       显然就是它在尝试插入时异或若干个数之后变成了0 00。

       那么就有如下式子:

       x  ^ d[a]^ d[b] ^ d[c] ^...=0 

       根据上面的那个小性质,则有:

       d[a] ^ d[b] ^ d[c] ^...=x

       所以,如果x不能成功插入线性基,一定是因为当前线性基里面的一些数异或起来可以等于x。

2、可以成功插入线性基

       我们假设x插入到了线性基的第i个位置,显然,它在插入前可能异或若干个数,那么就有:

       x ^ d[a] ^ d[b] ^ d[c]^ …=d[i]

       d[i]^ d[a]  ^ d[b] ^ d[c] ^ …=x

       所以显然,x此时也可以由线性基里面的若干个数异或得到。

综上,性质1得证

再看性质2

       各位大佬肯定认为这是一条显然的性质啊,但为了严谨一点,还是给出证明吧:

       我们使用反证法

       设d[a] ^ d[b]^ d[c]=0(其中d[c]比d[a]和d[b]要更晚被插入线性基)

       那么有d[a] ^ d[b]=d[c] 

       ∵d[c] 可以由d[a]^ d[b]得到

       ∴d[c]不可能插入线性基

       故假设不成立,所以线性基中不存在有任何数异或起来可以得到0。

最后看性质3

       这个性质被BJWC拿来出过一道题,那题网上几乎找不到证明(都是草草的给出做法然后贴代码),然而如果你熟记这个性质3,那么很快就能想明白那题。(这题在文章末尾会给出)

       那么我来尝试证明性质3(毕竟网上几乎找不到证明作为参考,这里笔者我只能乱推了):

还是没什么卵用地分类讨论一下


1、假如序列里面的所有元素都可以插入到线性基里面

       显然如果是这种情况的话,不管是用什么顺序将序列里的数插入到线性基里,线性基中的元素一定与原序列元素数量相同。所以性质3成立。

2、假如序列里面的一些元素不能插入到线性基里面

       我们设x不能插入到线性基里面,那么一定满足形如d[a] d[a]d[a] ^ d[b] d[b]d[b] ^ d[c]=x d[c]=xd[c]=x的式子,那我们尝试将插入顺序改变,变成:d[a]、d[b]、x、d[c]。那么显然,d[c]是不可能插入成功的,简单的证明:

       d[a]^ d[b] ^ d[c]=x

     d[a]^ d[b]^ x=d[c] (根据上面那条并没有什么卵用的异或性质)

       原来是x插入不进去,改变顺序后,d[c] d[c]d[c]插入不进去,也就是说,对于插入不进去的元素,改变插入顺序后,要么还是插入不进去,要么就是插入进去了,同时另一个原来插入的进去的元素插入不进去了,所以,可以插入进去的元素数量一定是固定的。

       显然,如果你去掉线性基里面的任意一个数,都会使得原序列里的一些(或一个)数无法通过用线性基里的元素异或得到,所以,每一个元素都是必要的,换句话说,这里面没有多余的元素,所以,这个线性基的元素个数在保持性质1的前提下,一定是最少的。

       顺便贴上插入的代码(代码里的ll 都是指longlong):

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


如何求最大值

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

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

       代码如下:

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


如何求最小值

       显然的,最小值一定是最小的d[i]。(如果让最小的d[i]去异或其它的d[i]一定会让它变得更大,所以它自己就是最小的)


如何求第k小的值

       完整的说,应该是——从一个序列中取任意个元素进行异或,求能异或出的所有数字中第k小的那个。

       首先,要对这个序列的线性基处理一下,对于每一个d[i] d[i]d[i],枚举j=i to1,如果d[i] (2)的第j位为1,那么d[i]异或d[j−1]。

       那么处理完一个线性基之后,应该大致是长这个样子的(x表示0或1):

       1xxxx0xxx0x

                1xxx0x

                        1x

       求解过程:将k先转成二进制,假如k的第i位为1,ans就异或上线性基中第i ii个元素(注意不是直接异或d[i-1])。

       代码如下:

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;
}
}

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

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


举报

相关推荐

0 条评论