线性基浅谈
一、线性基浅谈
在了解线性基之前,要简单理解什么是基。在线性代数中,基又称为基地,是刻画向量的工具。对于基底的元素我们称为基向量,向量空间的任意一个元素都可以唯一表示成为基向量的线性组合。同样,线性基也是一种基,它是一种特殊的基,一般用来求异或问题,所以在这里就先按照解决异或问题来讲述。
这种特殊的基是由64个数字或32个数字组成,具体是由几个数字组成要取决于,原题中给出的数字集合在二进制下的最高位,解释一下:若原题中的答案在int范围内,则运用32位线性基足以,若答案在long long范围内,就一定要用64线性基来解决,高精度另当别论。
现在对于n个数字(下面称这n个数为原数集):a1,a2……an,构建线性基,先不提线性基是如何构建出来的,先说一说线性基有什么性质:
1.原数集中的任意一个数字都能够通过线性基中的元素异或出来(这与线性基的构建方式有关)。
2.原数集中的数字异或出来的值域与线性基中的元素以后出来的值域相等(通过上一条性质可知)。
3.线性基中没有异或和为零的非空子集:现在假设存在这样一个子集使得b1,b2……bx的异或和为零,那么根据异或的性质能得出:b1=b2⊕b3⊕……⊕bx,既然b1已经能用除他之外的线性基元素表示出来,我们便没有必要再将b1放在线性集中。
4.线性基中的选取元素的每一种方案,都对应一个异或值,不存在多种选取方案对应同一个异或值的情况:现在假设存在这种情况,那么我们就会存在一个非空子集的异或值为零,这与上一条性质矛盾。
5.线性基是满足以上性质的最小集合,即线性基中不存在任何一个多余的元素。
二、线性基的构造
(在下面描述中,称线性基对应的第i位元素为placei)对于每一个数字num,从高位向低位扫,扫到第x位为1时,若当前placex有数字,则将num异或上placex,即使num=num⊕placex,并向下继续扫,若placex没有数字,则当前num就使placex,即placex=num。
下面有两种写法:第一种写法是按照上方描述的过程写的,第二种是以上方描述的过程为思想进行改进的,是所有n个数字同时插入。
1 2 3 4 5 6 7 8 | void insert( long long x) { for ( int i=63;~i;i--) if ((x>>i)&1ll) { if (!place[i]) {place[i]=x; return ;} else x^=place[i]; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | void insert( int *num) { for ( int j=63;~j;j--) { int p=0; for ( int i=1;(!p)&&i<=n;i++) if ((!v[i])&&((num[i]>>j)&1ll)) p=i; if (!p) {place[j]=0; continue ;} v[p]= true ,place[j]=num[p]; for ( int i=1;i<=n;i++) if (i!=p&&((num[i]>>j)&1ll)) num[i]^=num[p]; } } |
三、线性基应用
1.询问数字x是否在当前线性基异或集合中:
我们对于数字x从高位向低位扫,若数字x在二进制下的第i位为1,则异或上placei,若数字x最后异或出来等于零,则数字x在当前线性基的异或集合中,反之则不在。
1 2 3 4 5 6 | bool check( long long x) { for ( int i=63;~i;i--) if ((x>>i)&1ll) x^=place[i]; return x==0; } |
2.将线性基A和线性基B合并:
我们将线性基A中的每一个数字都依次插入到线性基B中即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | struct Base { long long place[N]; void insert( long long x) { for ( int i=63;~i;i--) if ((x>>i)&1ll) { if (!place[i]) {place[i]=x; return ;} else x^=place[i]; } } }; void merge(Base A,Base &B) { for ( int i=63;~i;i--) if (A.place[i]) B.insert(A.place[i]); } |
3.用线性基求当前数集能异或出来的最大数字:
我们对于当前数集构造线性基,因为二进制下高位的1对于答案的贡献要比下面所有的位数都为1的贡献还要大,所以我们可以进行贪心,若答案异或上当前位数上的数字比答案要大,则异或。
1 2 3 4 5 6 7 8 | long long mx() { long long ans=0; for ( int i=63;~i;i--) if ((ans^place[i])>place) ans^=place[i]; return ans; } |
4.用线性基求当前数集能异或出来的最小数字:
我们对于当前数集构造线性基,直接取出最后一位不是零的数字即可,为什么?仔细想想挺简单的。
1 2 3 4 5 6 | long long mn() { for ( int i=0;i<=63;i++) if (place[i]) return place[i]; return -1; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)