线性基
基本概念:
线性基的概念见:https://oi.men.ci/linear-basis-notes/
通俗的讲就是给定你由N个整数构成的整数序列,你可以从中选取一些(至少一个)进行异或((XOR))运算,从而得到很多不同的结果。你能将这(N)个整数压缩成另一个序列,取其中的进行异或((XOR)),得到的结果和之前相同,其中(a[i])二进制的最高位的(1)在第(i)位,且满足一下特殊的性质,这就是线性基。
构造线性基:
实现:将整数x插入线性基,将整数x二进制从最高位开始往后枚举,找到最高位i的"1",判断是否能插入bas[i],若不能将x^bas[i],然后继续往后找。
ll bas[N];
int sz=64;
bool insert(ll x){
for(int i=sz;i>=1;i--){
if((x&(1ll<<(i-1)))==0) continue;
if(bas[i]) x^=bas[i];
else bas[i]=x;return true;
}
return false;
}
高斯消元版。
bool insert(ll x){
for(int i=sz;i>=1;i--){
if((x&(1ll<<(i-1)))==0) continue;
if(bas[i]){
//若如主元j已经存在,用a[j]消去x的第j位然后继续
x^=bas[i];
continue;
}
for(int j=1;j<=i-1;j++){
//让x当主元i,需要先用第j(j<i)个主元消去x的第j位
if(x&(1ll<<(j-1))) x^=bas[j];
}
for(int j=i+1;j<=sz;j++){
//接着用x去消掉第j(j>i)个主元的第i位
if(bas[j]&(1ll<<(i-1))) bas[j]^=x;
}
bas[i]=x;
return true;
}
return false;
}
线性基相关性质
1.求任意子集xor最大值:把线性基中所有元素xor起来。
ll qryMax(){
ll ans=0;
for(int i=1;i<=sz;i++){
ans^=bas[i];
}
return ans;
}
2.求任意子集xor最小值: 等于最小的主元。
ll qryMin(){
for(int i=1;i<=sz;i++){
if(bas[i]) return bas[i];
}
}
3.查询第k小的值:把k进行二进制分解,把1对应位置的主元xor起来,注意这里第0小就是0。
//快速幂
ll ksm(ll x,ll p){
ll res=1;
while(p){
if(p%2==1) res*=x;
p/=2;
x=x*x;
}
return res;
}
ll qry(ll k){
if(ok) k--; //判断值域是否有0,有0则0为第1小
if(k<=0) return 0;
int cnt=count;//线性基大小
ll res=0;
for(int i=sz;i>=1;i--){
if(bas[i]==0) continue;
cnt--;
if(k-ksm(2,cnt)<0) continue;
res^=bas[i];//将k二进制拆分后1的位置
k-=ksm(2,cnt);
}
if(k) return -1;//不存在的k小
else return res;
}
4.查询x是否在值域中: 如果x能插入线性基,则x不能被当前线性基xor出来。
5.求任意子集与x进行xor的最大值:从高->低贪心,若xor上a[j]能变大就xor。
相关练习
【P3812 【模板】线性基】https://www.luogu.com.cn/problem/P3812
【P4570 [BJWC2011]元素】https://www.luogu.com.cn/problem/P4570
【acwing229 新nim游戏】https://www.acwing.com/problem/content/231/
【acwing210 异或运算】https://www.acwing.com/problem/content/212/
【牛客练习赛76 牛牛数数】https://ac.nowcoder.com/acm/problem/214945