- yeyou26 的博客
线性基 学习笔记
- 2024-1-23 20:39:22 @
基本
对于一个值域为1-N的集合S
它的线性基的值域与S相同
它的线性基中的元素个数小于等于logN
集合S中任意数异或和存在于线性基中
线性基任意数异或和存在于集合S中
插入
首先,线性基大体长这样
XXXXX 称为第[线性基中数的个数]个数
口XXXX
口口口XX
口口口口X 称为第1个数
d[i]和“线性基中第i个数”不是同一概念,注意辨析
将一个数x插入线性基成为一次插入
对于每次插入,从高到低枚举x的每一个二进制位,每一次枚举称为一次判断
对于每次判断,分三种情况讨论
1.x的第i位是0:continue掉这次判断
2.d[i]是空的:直接把d[i]变为x,结束插入
3.x的第i位是1且d[i]不是空的:x^=d[i]
void Insert(ll x)
{
for(ll i=64;i>=1;i--)
{
if(!x) return ;
if(!((x>>(i-1))&1)) continue;
if(!d[i])
{
d[i]=x;
total++;//线性基里一共有几个数
return ;
}
else x^=d[i];
}
}
查询异或最值
求集合S任意数异或最小值/最大值
最小值
分两种情况讨论:
1.线性基大小小于|S|:说明S中一定可以异或出0 最小异或和为0
2.线性大小等于|S|:显然,最小值为线性基中最小的数(无论怎么异或都会使值变大),从线性基的第1个位置往低N个位置枚举,直到一个有数的位置,这个数即为最小异或和
最大值
答案初始值为d[n]
从n-1到1枚举线性基中每一个数,如果答案异或这个数可以使答案变大,那么答案异或这个数,枚举完后的答案即为集合S异或最大值
查询第K小值
首先对线性基进行改造
具体地,先枚举i,再枚举j(从i-1到1)
如果d[i]的第j位为1,那么d[i]^=d[j]
显然,这样改造后的线性基仍是原S集合的线性基
void Remodle()
{
for(ll i=64;i>=1;i--)
{
for(ll j=i-1;j>=1;j--)
{
if((d[i]>>(j-1))&1) d[i]^=d[j];
}
}
}
而改造后的线性基大概长这样
1XXXX0XXX0X
000001XXX0X
00000000001X
所以又很显然(不会证,但是可以感觉出来)
令ans=0
假如询问的k的第i位为1,ans^=线性基中第i个数
最终ans即为第k小值
for(ll i=1;i<=64;i++)
{
if(d[i])
{
if(k%2) ans^=d[i];
k/=2;
}
}
查询x的排名
一样的,先改造线性基
从n枚举到1
如果x大于d[i],ans+=(1ull<<d[i]是线性基中第几个数-1) 并且 x^=d[i]
x即为线性基所有异或和中第ans小的数
ll cnt=total;//线性基里一共有几个数
for(ll i=64;i>=1;i--)
{
if(d[i])
{
if(x>=d[i]) ans^=(1ull<<(cnt-1)),x^=d[i];
cnt--;
}
}