线性基笔记

https://ouuan.github.io/线性基学习笔记/

概念

某种意义上来说线性基实际上是对原集合的压缩
感性的理解,基就是向量空间中的一个子集,它可以通过唯一的线性组合,来张成向量空间中所有的向量,这样就可以大大的缩小我们向量空间的大小。

性质

1,在线性基中任取若干个元素,它们的异或不为零。即它们线性无关。

2,其所在线性空间中每个元素都有唯一的方案由线性基中元素异或得到。

3,选取线性基中若干个元素异或起来得到一个元素,用这个元素去替换原线性基中任意一个元素,得到的新线性基张成的空间不变。

插入

bool insert(ll x)
{
	   for (int j = 60; ~j; --j)			//60为最大位数
	   {
	       if (((x >> j) & 1))
	       {
	           if (p[j]) x ^= p[j];
	           else
	           {
	               p[j] = x;
	               return true;
	           }
	       }
	   }
	   return false;
}

应用

判断一个数能否由若干数的子集异或得到

将这个数插入基中,如果能插入,则证明不能由这些数的子集异或得到

求最值

有n个元素,求其中若干个元素异或和的最大值
先求出线性基,然后从最高位开始,每一位都异或过去,如果比当前大就保留

ll ans=0;
for(int i=50;i>=0;i--)
{
     ans=max(ans,ans^p[i]);
}

求第k小子集异或和

构造特殊的线性基,满足线性基中有的位都只在线性基中的一个数中出现。

void insert(ll x)
{
   for (int j = 51; ~j; --j)
    {
        if ((x >> j) & 1)
        {
            if (p[j]) x ^= p[j];
            else
            {
                for (int k = j - 1; ~k; --k) if ((x >> k) & 1) x ^= p[k];
                for (int k = j + 1; k <= 60; ++k) if ((p[k] >> j) & 1) p[k] ^= x;
                p[j] = x;
                break;
            }
        }
    }
}

int main()
{
    int n,m;
    int cnt=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        ll a;
        cin>>a;
        insert(a);
    }
    for(int i=0;i<=51;i++)
    {
        if(p[i])
            px[cnt++]=p[i];							//取大于0的p[i]
    }
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        ll k;
        cin>>k;
         if (k >= (1ull << cnt) + (cnt != n))		//
        {
            puts("-1");
            continue;
        }
        if (cnt != n) --k;							//若存在异或和为0的情况,第1小则为0
        ll ans=0;
        for(int j=0;j<=cnt;j++)
        {
            if(k&1ll<<j)
                ans^=px[j];
        }
        printf("%lld\n",ans);
    }

}

结构体版本

struct linear_Bace;
typedef linear_Bace LB;
struct linear_Bace{
    int a[Len];
    int cnt;
    int& operator [](int idx){
        return a[idx];
    }
    int operator [](int idx)const{
        return a[idx];
    }
    void insert(int val){
        for(int i=Len-1;i>=0;i--){
            if((val>>i)&1){
                if(a[i]){
                    val^=a[i];
                }
                else
                {
                	a[i]=val;
                	cnt++;
                	break;
                }
            }
        }
    }
    bool find(int val){
        for(int i=Len-1;i>=0;i--){
            if((val>>i)&1){
                if(a[i]){
                    val^=a[i];
                }
                else
                    return 0;
            }
        }
        return 1;
    }
};
LB merge(LB a,LB b){					//线性基合并
    LB ans=a;
    for(int i=Len-1;i>=0;i--){
        if(b[i]==0)continue;
        ans.insert(b[i]);
    }
    return ans;
}
LB intersect(LB a,LB b){				//线性基求交
    LB ans= {},c=b,d=b;
    for(int i=0;i<Len;i++) {
        int x=a[i];
        if(!x)
            continue;
        int j=i;
        int T=0;
        for(; j>=0; --j){
            if((x>>j)&1)
                if(c[j]) {
                    x^=c[j];
                    T^=d[j];
                } else
                    break;
        }

        if(!x)
            ans[i]=T;
        else {
            c[j]=x;
            d[j]=T;
        }
    }
    return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值