线性基讲解
定义
设数集T的值域范围为[1,2^n−1]。
T的线性基是T的一个子集A={a1,a2,a3,...,an}。
A中元素互相xor所形成的异或集合,等价于原数集T的元素互相xor形成的异或集合。
可以理解为将原数集进行了压缩。
性质
1.设线性基的异或集合中不存在0。
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。
3.线性基二进制最高位互不相同。
4.如果线性基是满的,它的异或集合为[1,2^n−1]。
5.线性基中元素互相异或,异或集合不变。
维护
插入
如果向线性基中插入数x,从高位到低位扫描它为1的二进制位。
扫描到第i时,如果ai不存在,就令ai=x否则x=x⊗ai。
x的结局是,要么被扔进线性基,要么经过一系列操作过后,变成了0。

1 bool insert(long long val) 2 { 3 for (int i=60;i>=0;i--) 4 if (val&(1LL<<i)) 5 { 6 if (!a[i]) 7 { 8 a[i]=val; 9 break; 10 } 11 val^=a[i]; 12 } 13 return val>0; 14 }
合并
将一个线性基暴力插入另一个线性基即可。

1 L_B merge(const L_B &n1,const L_B &n2) 2 { 3 L_B ret=n1; 4 for (int i=0;i<=60;i++) 5 if (n2.d[i]) 6 ret.insert(n2.d[i]); 7 return ret; 8 }
查询
存在性
如果要查询x是否存于异或集合中。
从高位到低位扫描x的为1的二进制位。
扫描到第i位的时候x=x⊗ai
如果中途x变为了0,那么表示x存于线性基的异或集合中。
最大值
从高位到低位扫描线性基。
如果异或后可以使得答案变大,就异或到答案中去。

1 long long query_max() 2 { 3 long long ret=0; 4 for (int i=60;i>=0;i--) 5 if ((ret^d[i])>ret) 6 ret^=d[i]; 7 return ret; 8 }
最小值
最小值即为最低位上的线性基。

1 long long query_min() 2 { 3 for (int i=0;i<=60;i++) 4 if (d[i]) 5 return d[i]; 6 return 0; 7 }
k小值
根据性质3。
我们要将线性基改造成每一位相互独立。
具体操作就是如果i<j,aj的第i位是1,就将aj异或上ai。
经过一系列操作之后,对于二进制的某一位i。只有ai的这一位是1,其他都是0。
所以查询的时候将k二进制拆分,对于1的位,就异或上对应的线性基。
最终得出的答案就是k小值。

1 void rebuild() 2 { 3 for (int i=60;i>=0;i--) 4 for (int j=i-1;j>=0;j--) 5 if (d[i]&(1LL<<j)) 6 d[i]^=d[j]; 7 for (int i=0;i<=60;i++) 8 if (d[i]) 9 p[cnt++]=d[i]; 10 } 11 long long kthquery(long long k) 12 { 13 int ret=0; 14 if (k>=(1LL<<cnt)) 15 return -1; 16 for (int i=60;i>=0;i--) 17 if (k&(1LL<<i)) 18 ret^=p[i]; 19 return ret; 20 }
模板

1 struct L_B{ 2 long long d[61],p[61]; 3 int cnt; 4 L_B() 5 { 6 memset(d,0,sizeof(d)); 7 memset(p,0,sizeof(p)); 8 cnt=0; 9 } 10 bool insert(long long val) 11 { 12 for (int i=60;i>=0;i--) 13 if (val&(1LL<<i)) 14 { 15 if (!d[i]) 16 { 17 d[i]=val; 18 break; 19 } 20 val^=d[i]; 21 } 22 return val>0; 23 } 24 long long query_max() 25 { 26 long long ret=0; 27 for (int i=60;i>=0;i--) 28 if ((ret^d[i])>ret) 29 ret^=d[i]; 30 return ret; 31 } 32 long long query_min() 33 { 34 for (int i=0;i<=60;i++) 35 if (d[i]) 36 return d[i]; 37 return 0; 38 } 39 void rebuild() 40 { 41 for (int i=60;i>=0;i--) 42 for (int j=i-1;j>=0;j--) 43 if (d[i]&(1LL<<j)) 44 d[i]^=d[j]; 45 for (int i=0;i<=60;i++) 46 if (d[i]) 47 p[cnt++]=d[i]; 48 } 49 long long kthquery(long long k) 50 { 51 int ret=0; 52 if (k>=(1LL<<cnt)) 53 return -1; 54 for (int i=60;i>=0;i--) 55 if (k&(1LL<<i)) 56 ret^=p[i]; 57 return ret; 58 } 59 } 60 L_B merge(const L_B &n1,const L_B &n2) 61 { 62 L_B ret=n1; 63 for (int i=60;i>=0;i--) 64 if (n2.d[i]) 65 ret.insert(n1.d[i]); 66 return ret; 67 }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· C# 中比较实用的关键字,基础高频面试题!
· .NET 10 Preview 2 增强了 Blazor 和.NET MAUI
· 为什么AI教师难以实现
· 如何让低于1B参数的小型语言模型实现 100% 的准确率
· AI Agent爆火后,MCP协议为什么如此重要!