浅谈线性基

当给出多个数据,求异或时,通常会使用线性基进行处理。

线性基是什么?

对于原数组,我们试图用最少个数作为基础,用这些数之间的异或就可以表示子集合的异或和,而这些数即基础,称为线性基。

这种特殊的基通常是由32位数字或64位数字构成。具体多少位取决于题目中所给数字集合中二进制下最高位位数。

例如int范围需要32位线性基,而long long范围需要64位基解决。

-------------------------------------------------------------------------------------------------------------------------------

线性基常用函数

插入

我们用数组a表示线性基,a[i] 表示线性基当中最高位为第i位的基。

基的插入构造过程为:

1.如果当前数v的最高位i所在的基a[i]已经存在,即a[i]=0,那么这个数通过基a[i]转化为低位的数:v^=a[i];

2.如果a[i]不存在,就把当前数作为基a[i]。

例如现在给出如下例子1100,1011,1110,1101,0001,0101

1.1100,因为a[4]==0 所以a[4]=1100
2.1011,a[4]存在,所以1011->0111,故a[3]=0111
3.1110,a[4]存在,->0010,a[2]=0010
4.1101,a[4]存在,->0001,a[1]=0001
5.0001,a[1]存在,->0000
6.0101,a[3]存在,->0010,a[2]存在,->0000
当基满后,就可以表示所有数了

void insert(int v)
{
    for(int i=31;i>=1;i--)
    {
        int j=i-1;
        if(v&(1<<j))//判断v的位数,只有v的位数等于j,才会继续
        {
            if(!a[i])//当前a[i]为空
            {
                a[i]=v;
                break;
            }
            v^=a[i];
        }
    }
    if(v==0)
        a[0]=1;//可以通过异或和变为0,用于找最小值
}

-------------------------------------------------------------------------------------------------------------------------------

验证存在性

从高位到低位只要同位的最高位基存在就异或,到最后成功由这些基转化成0就说明存在。与插入的过程类似。

int findin(int v)
{
    if(v==0&&a[0])
        return 1;
    for(int i=31;i>=1;i--)
    {
        if(v&(1<<j))//先找到同位数的
        {
            v^=a[i];//异或
            if(!V)//异或完为0
                return 1;
        }
    }
    return 0;
}

-------------------------------------------------------------------------------------------------------------------------------

合并

把一个基作为基底,将另一个基的内容逐条插入即可(此处将a作为基底,b插入)。

void merge(int b[])
{
    for(int i=31;i>=1;i--)
    {
        if(!b[i])
            continue;
        insert(b[i]);
    }
}

-------------------------------------------------------------------------------------------------------------------------------

查找最大值

所有原数之间的异或和等价于基之间的异或和,所以找最大值,只需要判断所有基的取与不取即可找到最大值,从高位基开始找,取某个基时使答案变大,那么就是需要取的。

void findmax()
{
    int ans=0;
    for(int i=31;i>=1;i--)
    {
        if(ans^a[i]>ans)
        {
            ans^=ans;
        }
    }
    return ans;
}

-------------------------------------------------------------------------------------------------------------------------------

查找最小值

最小位数的基肯定就是答案(需要注意一个基都没有的情况)。

int findmin()
{
    if(a[0])
        return 0;
    for(int i=1;i<=31;i++)
    {
        if(a[i])
            return a[i];
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值