关于线性基的一些思考

2023大厂真题提交网址(含题解):

www.CodeFun2000.com(http://101.43.147.120/)

最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
在这里插入图片描述

前言:

对于大佬来讲这篇文章是无干货了。记录一下自己的理解。

从最开始去年集训的时候第一次听队长讲这个算法,懵懵懂懂,到后面第二次,第三次反复学习。直到今天的学习,我才真正的理解了线性基的原理。

学习好线性基的本质是:从线性代数的角度看问题,所以以下的文字中,我力求从线性代数的角度上描述问题.一旦碰到哪些概念混淆不清了,要么是我错了,要么是您需要马上捧起线性代数的书来好好再复习一下了。然后就是最好已经掌握高斯消元.

一.线性基是什么

线性基是 在系数域 F = { 0 , 1 } F=\{0,1\} F={0,1}下的一个向量空间 V V V.那么每个向量可以看作整数的二进制,而向量加法可以看作整数异或.

根据基的定义,我们知道线性基有如下几个特征:(这些性质都是基所具有的性质)

1.原序列里面的任意一个数都可以由线性基里面的一些数异或得到(表征整个向量空间)
2.线性基里面的任意一些数异或起来都不能得到 0 0 0. (线性无关)
3.线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的(向量空间的线性无关的极大集(即没有多余元素)).

二.如何求解线性基

这个问题就是在问我们:如何求解向量空间的基(先搞清楚怎么求这个)

那么做法就很显然了,我们利用类似高斯消元的方法将向量空间消解成上三角矩阵:

最开始是一个零矩阵,对于这 n n n个数 X X X.从第一行(最高位)开始,若 X X X的该位为1.则 若该位值为0,则将 X X X插入本行,否则异或完本行的值继续往下一行走,直到插入.

注意:如果理解不了这个过程,你就先将这 n n n个数展开成矩阵,然后用朴素的高斯消元的方法求一遍,你就知道为什么这么做是对的了。只是用这种方法比直接高斯消元快很多.这里我无法用简短的文字描述,就不写清楚了。

经过上面的过程我们容易发现以下几点性质:

1.1若存在不能插入到线性基里的数 a i a_i ai.那么这个 a i a_i ai能够被 [ 1 , i − 1 ] [1,i-1] [1,i1]里的一个子集异或出来.反之不行.
1.2若存在不能插入到线性基里的数 a i a_i ai,那么该序列能够异或出 0 0 0.
1.3根据抽屉原理:若 n > 63 n > 63 n>63,那么该序列一定存在一个子集使得异或为0.
反证:若不存在,那代表每个数都插入成功了,那么就会有 n > 63 n>63 n>63个基,但是实际上只有63个。所以假设不成立.
1.3的另一种表述:当 n > 63 n>63 n>63时,该序列一定不是线性无关组.
2.线性基无法异或得到 0 0 0

三.线性基的作用:

1.求最大值:直接高位贪心求.正确性显然

2.求最小值:直接低位即可,但是注意,若原序列可以异或得到0,那么则需要考虑0.

3.求是否能够异或出某个数:利用线性基性质1.1,尝试将其插入到线性基。若插入的进去,则不能,反之可以。

4.求线性基能够异或出多少个本质不同的数出来:数对角线上 1 1 1的个数(线性基的大小)为 c n t cnt cnt。答案即为: 2 c n t 2^{cnt} 2cnt.因为一行选或不选会直接导致某一位的值是 0 0 0还是 1 1 1

5:求第k小

1.重构线性基:
用类似高斯-约旦消元法的思想,将线性基消元成对角型.即每一列至多只会有一个1(其他的1都被消掉了).

做法:对于第 i i i行,遍历第 [ i − 1 → 0 ] [i-1 \rightarrow 0] [i10].若 b [ i ] 与 ( 1 < < j ) = = 1 b[i] 与(1<<j) ==1 b[i](1<<j)==1,那么 b [ i ] = b [ i ] ⊕ b [ j ] b[i] =b[i]\oplus b[j] b[i]=b[i]b[j].

这个时候每一个基的作用就只是独立的表征一个或一些位,任意两个基之间的异或 不再会在任何一位上产生 1 ⊕ 1 1 \oplus1 11的情况了。

例如:
[ 1 1 0 0 1 1 0 0 1 ] \begin{bmatrix} 1& 1 &0\\ 0& 1 &1 \\ 0& 0& 1 \end{bmatrix} 100110011

重构之后:

[ 1 0 0 0 1 0 0 0 1 ] \begin{bmatrix} 1& 0 &0\\ 0& 1 &0 \\ 0& 0& 1 \end{bmatrix} 100010001

重构之后线性基的性质不变,而且还是对应的原来序列的线性基。

这个时候显然我们只需要将 k k k拆成二进制。然后如果第 i i i位是1,那么就将答案异或上第 i i i个基.(注意不是直接异或 b [ i − 1 ] b[i-1] b[i1]).

正确性就很显然了,因为只要它们的 1 1 1之间一一对应,那么就是等价的。(基的选或不选的性质完美对应了 k k k的二进制形式)。大家意会一下(

基本模板:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
ll b[66];
int n , cnt;
int up = 63;
void Insert (ll x)
{
    for (int i = up ; i >= 0 ; i--){
        if ((x & (1ll << i)) == 0) continue;
        if (b[i]) x ^= b[i];
        else {
            b[i] = x;
            break;
        }
    }
}
ll getMax ()
{
    ll res = 0;
    for (int i = up ; i >= 0 ; i--) res = max (res , res ^ b[i]);
    return res;
}
// 消元重构
void rebuild()
{
    for (int i = up ; i >= 0 ; i--){
        if (!b[i]) continue;
        for (int j = i - 1 ; j >= 0 ; j--)
            if (b[i] & (1 << j))
                b[i] ^= b[j];
    }
}
ll ask (ll k)
{
    if(k==1&&cnt<n)return 0;//特判一下,假如k=1,并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
    if(cnt<n)k--;//类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
    if (k >= (1ll << cnt)) return -1;
    ll ans = 0;
    for (int i = 0 ; i <= up ; i++){
        if (b[i]){
            if (k & 1) ans ^= b[i];
            k >>= 1;
        }
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int t; cin >> t;
    int tt = 0;
    while (t--){
        memset(b , 0 , sizeof b);
        cnt = 0;
        cin >> n;
        for (int i = 1 ; i <= n ; i++){
            ll x; cin >> x;
            Insert(x);
        }
        for (int i = 0 ; i <= up ; i++) cnt += (b[i] != 0);
        rebuild();
        int q; cin >> q;
        cout << "Case #" << (++tt) << ":" << endl;
        for (int i = 1 ; i <= q ; i++){
            ll k; cin >> k;
            cout << ask (k) << endl;
        }
    }
    return 0;
}
进阶骚操作:

6.线段树维护线性基并
很简单,将左儿子的线性基依次插入到右儿子的线性基中即可.
7.线段树维护线性基的交
https://blog.csdn.net/TDD_Master/article/details/97965051?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control
8.线性基的删除操作

参考博客:
https://ksmeow.moe/linear_basis/
https://blog.csdn.net/a_forever_dream/article/details/83654397

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值