首页 > 其他 > 详细

线性基

时间:2018-12-22 10:45:10      阅读:141      评论:0      收藏:0      [点我收藏+]

线性基

本文在学习了博客线性基学习笔记,总结自己的一些理解

处理的问题

对于一组数,通过构造一组线性无关的基底来求解异或最值、异或第k小值以及某值是否可得到的问题。

构造方法

维护一组线性基:对于每一个给定的数,对其二进制表示,从高位到低位扫描,当发现第x位是1时,若当前最高位为x的基底(即bs[x])为空,则bs[x]置为该数,否则将该数异或上bs[x],继续往下扫描。

ll bs[70];
void insert(ll x)
{
    for (int i=62;i>=0;--i)
    {
        if (x&(1ll<<i))
        {
            if (!bs[i])
            {
                bs[i]=x;
                return;
            }
            x^=bs[i];
        }
    }
}

原理


对于给出的数,将其二进制表示看成一个向量,对这一组向量,按上述方法,构造出一组线性无关基底,满足下列性质:

  • 最高位1的位置互不相同
  • 该组基底能异或出的值的集合,与原来的那组数能异或出的值的集合,相同
  • 任意一个可以用这些向量组合出的向量x,组合方式唯一


对于性质一,容易看出按照上述方法可以构造出来。
对于性质二,这是后面用来求解异或最值等问题的正确性的保证。证明这个性质的大概思想:令c=a xor b,则a和c可以得到b,则c可以代替b,加入本该是a、b的组中。
对于性质三,参考开头提到博客的证明方法:
假设x的组合方法不唯一, 也就是说 x=a1 xor a2 ? ap = b1 xor b2 ? bq
那么x xor x = 0 = a1 xor a2 ? ap xor b1 xor b2 ? bq
也就是说 可以用这个向量组里的向量组合出0向量, 与线性无关矛盾。 故组合方法唯一。

应用

求解一组数异或最值问题

求最大值:从最高位开始,若异或上该基底可以使当前值变大,则异或。做到最低位结束,得到即为答案

ll max_qry(ll init=0) // init为初始值,若没有,则置为0;
{
    for (int i=62;i>=0;--i)
    {
        if ((init^bs[i])<init)
            init^=bs[i];
    }
    return init;
}

求最小值:从低位往上找到的第一个非零基底。这里特别提一下,若有初始值,则应该仿照上述求最大值的方法,从高位开始贪心异或,能使其变小就异或上。

ll min_qry(ll init=0)
{
    if (init==0)
    {
        for (int i=0;i<=62;++i)
            if (bs[i])
                return bs[i];
    }
    else
    {
        for (int i=62;i>=0;--i)
            if ((init^bs[i])<init)
                init^=bs[i];
        return init;
    }
}

判断某数是否可以由某组数异或出来

从高位向低位扫这个数的二进制表示,碰到值为1的位,则异或上最高位为这个位的基底。若最后该数变为0,则表示该数可以被这组基的子集构造出来。结合性质三感受一下。

bool check(ll x)
{
    for (int i=62;i>=0;--i)
        if (x&(1ll<<i))
            x^=bs[i];
    return x==0;
}

查询可异或出的第k小值

在原有线性基的基础上,重新构造一组基底。这组基底相比于原来的,每个基底变为通过异或得到的包含原来最高位的最小值。再对k二进制表示,异或上对应位的新基底即可。这里特别提一下,0的情况。如果构造基底的个数少于题目给定的数的个数,说明0使可以被异或出来的。那么对于k,要使k=k-1。
方法如下

vector<ll> tmp;
ll kth_query(int k)
{
    for(int i=0;i<=62;++i)
    {
        if (!bs[i])
            continue;
        for(int j=i-1;j>=0;--j)
            if(bs[i]&(1ll<<j))
                bs[i]^=bs[j];
        tmp.pb(bs[i]);
    }
    if (k>=(1<<sz(tmp)))
        return -1;
    ll res=0;
    for(int i=0;i<sz(tmp);++i)
        if(k&(1ll<<i))
            res^=tmp[i];
    return res;
}

线性基

原文:https://www.cnblogs.com/orangee/p/10159717.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!