ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[学习笔记]线性基

2022-07-27 19:02:42  阅读:159  来源: 互联网

标签:int qquad 笔记 学习 异或 base 线性 quad


学长讲的,之前也在线性代数上看到过但这两个不是一个东西

所以我自然结合了线性代数来乱搞(

约定用 $\oplus$ 表示异或

1. 什么是线性基?

$\quad$ 线性基是一个数的集合,每一个序列都至少有一个线性基,取线性基中的若干个数异或起来可以得到序列中的任意数字

$\quad$ 为方便理解,可以将一个数字拆成二进制

$\quad$ 考虑一个数字 $13$ ,把它拆成二进制就是 $(1101)_2$ ,设有 $m$ 位

$\quad$ 那么考虑建系,构建一个 $m$ 维直角坐标系 $\Re_m$ ,$13$ 可以表示为 $\vec{a}=(1,1,0,1)$

$\quad$ 能够把这个向量唯一表示的一组基底(比如 $12=(1100)_2$ 和 $1=(1)_2$ )就叫做线性基

 

2. 线性基的性质

$\quad$ $\mathfrak{1.}$ 原序列里的任何一个数都可以由线性基中的几个数异或得到

$\quad$ $\mathfrak{2.}$ 线性基中的任意一些数异或起来都不能得到 $0$

$\quad$ $\mathfrak{3.}$ 线性基中的数字个数是唯一的且尽可能少

$\qquad$ 对于性质 $\mathfrak{2}$ 的证明:首先假设 $a\oplus b\oplus c=0$ ,那么 $a\oplus b=c$ ,根据定义得不成立

$\qquad$ 对于性质 $\mathfrak{3}$ 的证明:依旧考虑 $\Re_m$ 空间中的一些基底,显然地,少了任意一个都会导致某些向量无法表示

$\qquad\qquad\qquad\qquad\qquad$ 同时另外的任何一个数都可以用这一组基底表示,因此多出一个是完全没有必要的

3. 构建一个线性基

$\quad$ 不妨设 $base$ 数组为 $a$ 数组的一个线性基,那么我们考虑如何构建这个玩意?

$\quad$ 显然的是可以对于 $a$ 中的每一个数从二进制的最高位向最低位扫,如果这一位还没有对应的线性基那么就把当前的 $a[i]$ 作为线性基中的一员

$\quad$ 在插入一个数字之后立刻 $\mathbf{break}$ 是一个比较重要的点

 

#define int long long
void insrt(int x){
    for(int i=63;i>=0;i--){
        if((x>>i)&1){
            if(!base[i]){
                base[i]=x;
                return;
            }else{
                x^=base[i];
            }
        }
    }
}

 

4. 线性基的运算

$\;$ 0.判断一个数能否用线性基表示

$\quad$ 从高位到低位减去对应的 $base[i]$ 就好了,代码就不放了

$\;$ 1. 查询异或最大值

$\quad$ 放一道例题

$\quad$ 这道题目就是板子,题解中大多在构造线性基的时候判断 $x$ 和 $(1<<i)$ 的关系,但是当 $i=63$ 时 $\mathbf{long}$ $\mathbf{long}$ 有溢出的风险

$\quad$ 所以我采用了 $(x>>i)$ 的方式避免溢出

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1010;
int n,ans;
int base[WR],a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void insrt(int x){
    for(int i=63;i>=0;i--){
        if((x>>i)&1){
            if(!base[i]){
                base[i]=x;
                return;
            }else{
                x^=base[i];
            }
        }
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        insrt(a[i]);
    }
    for(int i=63;i>=0;i--){
        if((ans^base[i])>ans) ans^=base[i];
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

$\;$ 2.查询异或最小值

$\quad$ 一般而言,异或的最小值就是线性基中的最小元素,这是因为我们总是尽可能地让这一位以上的各位是 $0$ 再去插入

$\quad$ 但是要注意特殊情况也就是存在 $0$ ,此时直接输出 $0$ 就行了

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1010;
int n,ans;
int base[WR],a[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
void insrt(int x){
    for(int i=63;i>=0;i--){
        if((x>>i)&1){
            if(!base[i]){
                base[i]=x;
                return;
            }else{
                x^=base[i];
            }
        }
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        insrt(a[i]);
        if(a[i]==0){
            printf("0\n");
            return 0;
        }
    }
    for(int i=0;i<=63;i++){
        if(base[i]){
            printf("%lld\n",base[i]);
            return 0;
        }
    }
    return 0;
}
View Code

 

$\;$ 3. 查询异或排名

$\quad$ 有一道例题 albus就要第一个出场

$\quad$ 这里给出正确性的证明:

$\qquad$ 首先,$n$ 个数任意排列组合有 $\sum\limits_{i=0}^{n}C_{i}^{n} =2^n$ 种不同的异或方法

$\qquad$ 但是有些数异或的结果是 $0$ ,这样就导致了重复结果的出现

$\qquad$ 由于线性基里异或不可能为 $0$ ,设线性基大小为 $V$ 那么有 $2^V$ 种不同的异或值

$\qquad$ 接着,对于一个异或和,在外面 $n-V$ 个数中任意取一个数加入可以异或出这个异或和的数的集合,一定可以通过调整原本的线性基内的数的组成来使异或和不变。

$\qquad$ 还是分类讨论:

$\qquad\quad$ $\mathfrak{1}.$ 如果从外面加入的这个数可以用除了表示出这个异或和的那些线性基内的数表示出来,那就把那些数也异或进来,那么异或和不变。

$\qquad\quad$ $\mathfrak{2}.$ 如果外面加入的数必须用完全属于表示出这个异或和的那些线性基内的数表示出来,那就把这些线性基中的数弹掉,那异或和还是不变。

$\qquad\quad$ $\mathfrak{3}.$ 如果部分属于表示这个集合的数,部分不属于,那就把两个部分拆开,属于的部分不选,不属于的部分选,就把这两个部分都变成了 $0$ ,异或和照样不变。

$\qquad$ 假如从 $n-V$ 个数中选了多于一个数,对于每一个数都按照上面的操作方法就可以保证异或和不变。

$\qquad$ 所以对于外面的点的任意一个集合都是可以通过调整线性基内的数使得异或和不变。所以同一个异或和出现了 $2^{n-V}$ 次

$\qquad$ 那么如何计算就比较简单了,我们找出唯一构成 $q$ 的方案,用一个数组存储

$\qquad$ 考虑到小于 $q$ 的子集至少都要不选取这些元素中的其中一些,我们枚举这些元素中不选取的位数最高的元素(假设为从低到高为第 $i$ 位存在的元素)

$\qquad$ 那么位数更低的元素显然可以随便选,也就是说对 $rank$ 的贡献是 $2^i\times 2^{n-V}$ ,求个和就好了

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=100100,mod=10086;
int n,ans,q;
int stk[WR],cnt;
int rnk;
int base[WR],val[WR];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
int quick_pow(int a,int b){
    int bse=a,res=1;
    while(b){
        if(b&1) res=res*bse%mod;
        bse=bse*bse%mod;
        b>>=1;
    }
    return res;
}
void insrt(int x){
    for(int i=31;i>=0;i--){
        if((x>>i)&1){
            if(!base[i]){
                base[i]=x;
                return;
            }else{
                x^=base[i];
            }
        }
    }
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        int x=read();
        insrt(x);
    }
    q=read();
    for(int i=0;i<=31;i++){
        if(base[i]) stk[++cnt]=i;
    }
    for(int i=1;i<=cnt;i++){
        //printf("%lld ",stk[i]);
        if((q>>stk[i])&1) rnk=(rnk+quick_pow(2,i))%mod;
    }
    printf("%lld\n",(rnk*quick_pow(2,n-cnt-1)%mod+1)%mod);
    return 0;
}
View Code

 

标签:int,qquad,笔记,学习,异或,base,线性,quad
来源: https://www.cnblogs.com/WintersRain/p/16517012.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有