[题解] [笔记] 洛谷-线性基 & 线性基学习笔记
原题链
算法简介
基本概念
1.向量:在信息学中,向量通常用来表示一个点的集合,在坐标系(不一定是二维)中可以表示一个点的坐标,形如.
2.基:举个栗子来说明,对于一个平面直角坐标系,我们规定沿(x)轴正方向长度为(1)的向量为(i-hat),沿(y)轴正方向长度为(1)的向量为(j-hat),在这个二维平面内任意其它向量都可以由这个向量进行缩放得出,因此我们称(i-hat)与(j-hat)为一对基.从以上文字中我们可以得知基是向量空间的一个子集,利用子集中的元素可以表达整个空间,而特别的,对于一个二维平面,这个子集只包含两个元素即我们上文所提到的一对基,其他任意向量都可以又这一对基变换得到.
3.线性相关&线性无关:如果没有一个向量不能由其它向量表示,则称为线性无关,否则称为线性相关.在二维平面中,(i-hat)和(j-hat)是线性无关,其余都是线性相关的.
对于此题
这个题的基个数不一定只有(2)个,这个题找基的关键是判断当前这个元素能否被之前的元素凑出来,如果可以则说明他们线性相关,不是基,否则则是线性无关,是基.这样我们就减少了很多无效数字(称之为无效是因为他们可以有其它线性无关的数通过异或操作表示出来).
对于程序中一些变量和语句的解释
1.t数组:这个题给出数据范围(0≤Si≤2^{50}),由于二进制的性质,可以知道(Si)的位数不超过(50)位(手推几个就知道了),(t)数组表示的从高到低的二进制位.(t[i])表示的是第一个二进制位(i)为(1)的数,若果不存在则(t[i] = 0).由于(t[i])表示的是第一个二进制位(i)为(1)的数,所以可以将它视为一个基,在考虑如果当前(t[i])已经有数值了,若输入的(x)二进制第(i)位也为(1)则不确定(x)能否成为基,因为尽管二进制(i)位及之前的几位已经可以被表示,但是后面的位并不知道,因此让(x) ^= (t[i]),使(x)的二进制第(i)位为(0),并接着继续判断后面的位;在考虑如果(t[i] = 0),则说明之前不存在二进制第(i)位为(1)的数,而如果刚好(x)的二进制第(i)位为(1),则让(t[i] = x),并退出循环.
2.对于求解部分:我们知道对于一个二进制数,如果高位比另一个数高,即对于同一位一个数为(1),另一个数为(0),则这一位为(1)的数一定更大.而我们知道除了(t)数组里保存的基,其它任意输入的数均可有求出的基通过异或得出,所以不用担心会漏解,或解不优.又因为上文说过,高位大的二进制数一定大,所以我们可以贪心,从高位较大的基往下枚举(说白了就是从高位为(1)的基一直和(ans)进行异或,如果使(ans)变大则更新(ans)).
文字说明有点晕,上代码
#include <bits/stdc++.h>
using namespace std;
#define LL long long
long long t[35];
int main(){
long long n;
scanf("%lld",&n);
for(long long k = 1;k <= n;k++){
long long tmp;
scanf("%lld",&tmp);
for(int i = 60;i >= 0;i--){//从高位往低位枚举,判断是否为基
if(tmp & (1LL << i)){
if(t[i])
tmp ^= t[i];
else {
t[i] = tmp;
break;
}
}
}
}
long long ans = 0;
for(int i = 60;i >= 0;i--){//贪心求答案
if((t[i] ^ ans) > ans)
ans ^= t[i];
}
printf("%lld
",ans);
return 0;
}