线性基详解+例题
线性基
线性基是一个能够在每次时间复杂度\(O(\log_2d)\),d是数字的位数内处理异或最大最小的数据结构。
问题:请你维护一个数据结构,支持插入一个数,求这些数任意异或得到的结果是否可能为某一个数、最大值、最小值、第k小值。
做法:开一个数组a[MAXN],MAXN是数字最高位数。
a[i]表示当前线性基内任意异或出来的数字中,最高位为i的任意一个数字。
#define MAXN 60
long long a[64];
插入x|判断x是否可以由线性基得到
用下面的数举例
x1:10101 x2:10001 x3:11000 x4:11011 x5:10111 x6:01101
从 高位到低位 枚举所有位数,如果x的第i位为0则不管,如果x的第i位为1:
1.如果a[i]不存在,更新线性基 a[i]=x,并退出。
2.如果a[i]存在,令x^=a[i]。
如果最后x变成了0,那么说明x可以由线性基异或得到。
void Insert(long long x)
{
for(int i = MAXN; i >= 0; i--)
{
if(x & (1LL << i))
{
if(!a[i]){a[i] = x;return;}
x ^= a[i];
}
}
}
查询最大值
从高位到低位扫描线性基。如果异或之后答案变大,就把这一位异或到答案。
long long getmax()
{
long long ans = 0;
for(int i = MAXN; i >= 0; i--)
{
if((ans ^ a[i]) > ans)
ans ^= a[i];
}
return ans;
}
查询最小值
要先判断一下是否可能是0。
不可能是0则像下面这样从低位到高位扫描线性基。最低位上的线性基即为答案。
long long getmin()
{
for(int i = 0; i <= MAXN; i++)
{
if(a[i] > 0)
return a[i];
}
}
模板
首先构建线性基
然后从高位往低位,只要能变成1就变成1
因为只要高位有1,就会比高位是0更优,就像二进制下的 1000...(n个0)>0111...(n个1)
第k小
首先我们要改造一下线性基。
我们把线性基改造成每一位相互独立,意思就是对于第i位,线性基上只有一个未知的第i位可能是1。
具体如何改造,就是从高位向低位扫描,对于第i位线性基a[i],对于j<i,如果a[i]的第j位是1,就让a[j]异或上a[i]。
(可以发现,构造线性基的过程类似于高斯消元的消元过程,改造线性基的过程类似于高斯消元的回代过程)
查询的时候,将k进行二进制拆分,对于的1位,就异或对应的线性基。
最终得到的答案是第k小值。
int p[64],cnt;
void rebuild()
{
for(int i = MAXN; i >= 0; i--)
{
for(int j = i - 1; j >= 0; j--)
if(a[i] & (1LL << j))
a[i] ^= a[j];
}
for(int i = 0; i <= MAXN; i++)
{
if(a[i])
p[cnt++]=d[i];
}
}
long long query(long long k)
{
long long ans=0;
rebuild();
if(k >= (1LL << cnt))
return -1;
for(int i = 0; i <= MAXN; ++i)
{
if(k & (1LL << i))
ans ^= p[i];
}
return ans;
}