线性基简单学习笔记

线性基

  • 百度百科【IN
    百度的第一句话似乎有点怪异

其实,对于广义的线性基,也就是线性代数里面的。一个基也相当于该空间内的一个集合。

其实一个基的最大的特点就是:

  1. 它可以线性表出所有该空间里的向量(元素)
  2. 它的大小是最小的(也就是基中的元素个数不多)
  3. 且它的任何一个非空子集不会线性表出空集。

对于线性表出的意思,可以简单的理解为一些元素通过一种运算可以得到另一个元素,就称这些元素线性表出了另一个元素(线性表示出的意思)

关于第三点,可以简单证明一下,用反证法:
假如,对于 ⨁ i = 1 n a i = ∅ \bigoplus_{i=1}^na_i=\varnothing i=1nai=,那么我们将左边的一个元素移动到右边,则变成了 ⨁ i = 1 n a i [ i ̸ = j ] = a j \bigoplus_{i=1}^na_i[i\not= j]=a_j i=1nai[i̸=j]=aj,那么 a j a_j aj这个元素就可以被 ⨁ i = 1 n a i [ i ̸ = j ] \bigoplus_{i=1}^na_i[i\not= j] i=1nai[i̸=j]线性表出了,所以将 a j a_j aj删除,剩下的才是线性基。

这里,我们只讲在异或意义下的线性基,也就是 ⨁ = x o r \bigoplus=xor =xor


除了对于数在二进制下的异或,我们可以将其推广到集合上,也就变成了对称差,那么可以用同样的方式对于一系列集合求其线性基。


一些定义:

  • 异或和

这里我们的异或和不是 a i   x o r   a j + a k   x o r   a w ⋯ a_i\ xor\ a_j +a_k\ xor\ a_w\cdots ai xor aj+ak xor aw,而是 x o r i = 1 n a i {xor}_{i=1}^na_i xori=1nai,也就是所有的 a i a_i ai异或起来。其中 a i a_i ai为无符号的整数类型。

  • 线性相关

学过线性代数的人应该知道吧QAQ
对于一个集合 S S S和集合中的任意一个元素 s j s_j sj,这个 s j s_j sj可以由集合中的其它元素线性表出,那么就称集合 S S S线性相关,反之就如果所有的 s i s_i si都不满足该性质,就称为线性无关。

  • 张成

对于一个集合 S S S的张成,我们简记为 s p a n ( S ) \rm span(S) span(S),其中:
s p a n ( S ) = { t ∣ t = ⨁ s i ∈ S ′ s i } ( S ′ 为 S 的 子 集 ) \rm span(S)=\{t|t=\bigoplus_{s_i\in S^{'}}s_i\}(S^{'}为S的子集) span(S)={tt=siSsi}(SS)
用文字来说,就是对于每一个 S S S的子集的线性运算结果所组成的集合称作这个集合的张成。

一个结论就是对于一个线性相关的集合 S S S,去除其中任意一个元素,它的张成不变(因为这个去除的元素可以由其他的表出),这个也可以来证明最开始的特点3。


这里我们来定义线性基:

线性基

我们称一个集合(基) X X X S S S的线性基,当且仅当:

  1. X X X是线性无关的(如果线性相关的话,我们可以将一些可以表出的元素删除,因为要保证最小,所以必须线性无关)
  2. S ⊆ s p a n ( B ) S\subseteq \rm span(B) Sspan(B),也就是这个集合 S S S可以由 X X X线性表出( S S S X X X的张成子集)。
  • 性质
  1. X X X是最小的满足条件的集合,它的任何真子集都不可能是新的线性基,因为少了任何一个元素,那么由于线性无关,这个元素就不能被剩下的表出了,那么就不符合条件2。
  2. S S S中的任意元素都可以被 X X X线性表出。(其实也就是条件2)

构造

我们这里只讲在异或的运算下如何快速构造:

对于一个无符号(非负数)整数集 S S S,我们令它的元素最大二进制位的个数为 L L L,我们可以发现,只需对于每一位存储一个元素,那么就可以表出所有的了,所以我们可以构造一个 l o g ( max ⁡ { s i } ) log(\max\{s_i\}) log(max{si})大小的线性基。

构造方式为将每个元素 s i s_i si插入线性基,那么复杂度就为 O ( ∣ S ∣ l o g ( max ⁡ { s i } ) ) O(|S|log(\max\{s_i\})) O(Slog(max{si}))

所以我们令 X X X为其线性基,那么可以安照如下方式构造:

  • 从高位到低位扫描 s i s_i si
  1. x i = 0 x_i=0 xi=0时且 s i s_i si i i i位为 1 1 1,那么我们令 x i = s i x_i=s_i xi=si,然后即可停止扫描,因为到插入 s i s_i si为止,只有 s i s_i si能表出第 i i i位的 1 1 1
  2. x i ̸ = 0 x_i\not=0 xi̸=0时,由于这一位已经可以表出了,所以我们将 s i s_i si异或上这一位的值,继续扫描(因为要保证可以表出每一位且线性无关)。

那么构造方式就是这样,代码实现非常简单,如下:

void insert(int si){
	for(int i=maxbit;i>=0;i--){
		if(!(si>>i)&1) continue;
		if(!X[i]){X[i]=si;return;}
		si^=X[i];
	}
}

对于合并两个线性基,就是将一个线性基中的元素插入另一个,复杂度为 ( l o g ( max ⁡ { s i } ) ) 2 (log(\max\{s_i\}))^2 (log(max{si}))2


应用

  1. 给出你一个集合 S S S,和一个数字 a a a,让你求 a   x o r   s i a\ xor\ s_i a xor si最大:其实这个就扫一遍 s i s_i si即可,不用线性基。
  2. 给出你一个集合 S S S,和一个数字 a a a,让你判断这个集合能否表出(异或出) a a a:先求出线性基,我们就从二进制高位到低位扫描,如果第 i i i位为 1 1 1,那么就将 a   x o r   x i a\ xor\ x_i a xor xi,如果最终 a = 0 a=0 a=0则表示可以(也就是看能否插入线性基,如果不能插入则就可以表示出来,根据线性基的定义即可知道)。
  3. 给你一个集合 S S S,求出这个集合能够异或出的最大值:我们求出 S S S的线性基,然后扫一遍,每次取 a n s = max ⁡ { a n s , a n s   x o r   x i } ans=\max\{ans,ans\ xor\ x_i\} ans=max{ans,ans xor xi}即为答案。
  4. 给你一个集合 S S S,求这个集合能否线性表出一个元素 a a a:就是把 a a a带进去,每一位能消除就消掉,如果最后变成0了就说明可以,否则就不行。
  5. 求异或的第 k k k大【题目地址】:将 k k k按位拆分,贪心即可
//话说loj的格式化代码的功能真好用OvO
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int M = 55;
int n, m;
ll bit[M], up;
void insert(ll a) {
    for (int i = M - 1; i >= 0; i--) {
        if ((a >> i) & 1) {
            if (!bit[i]) {
                bit[i] = a;
                ++up;
                return;
            } else
                a ^= bit[i];
        }
    }
}
void calc(ll a) {
    if (a > (1ll << up)) {
        puts("-1");
        return;
    }
    ll ans = 0, tot = up;
    a -= (up < n);
    for (int i = M - 1; i >= 0; i--) {
        if (bit[i]) {
            if (((ans >> i) & 1) ^ ((a >> (--tot)) & 1))
                ans ^= bit[i];
        }
    }
    printf("%lld\n", ans);
}
ll x;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &x);
        insert(x);
    }
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%lld", &x);
        calc(x);
    }
    return 0;
}
  1. 合并线性基

其实除了异或的线性基,还有实数域上的线性基

实数线性基-例题


End

若有错,请及时提出联系博主
萌新才学QWQ


洛谷模板IN

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=65;
ll rec[M],a,ans;
void insert(ll a){
	for(int i=61;i>=0;i--){
		if(!(a>>i)&1) continue;
		if(!rec[i]){rec[i]=a;return;}
		a^=rec[i];
	}
}
ll getmax(){
	for(int i=61;i>=0;i--)ans=max(ans,ans^rec[i]);
	printf("%lld\n",ans);
}
int n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a),insert(a);
	getmax();
	return 0;
}

比较正规的构造方法:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int B = 61;
int n;ll a[B+1];
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        ll x;
        scanf("%lld", &x);
        for (int bit = B; bit >= 0; bit--) {
            if ((x >> bit) & 1) {
                if (a[bit]) x ^= a[bit];
                else {
                    a[bit] = x;
                    break;
                }
            }
        }
    }
    for (int i = 0; i <= B; i++) {
        for (int j = 0; j < i; j++) {
            if ((a[i] >> j) & 1) a[i] ^= a[j];
        }//本质类似于高斯消元
    }
    ll ans = 0;
    for (int i = 0; i <= B; i++) ans ^= a[i];//这样构造出来的就不用每次取max了
    printf("%lld\n", ans);
    return 0;
}

参考学习博客:
友链-Althen
menci-Orz%%%
ljh2000-Orz%%%

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VictoryCzt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值