线性基小结
oiwiki
性质:
- 线性基内的元素异或起来的到的值就是原集合异或起来得到的值.
- 线性基是满足性质一的最小的集合.
- 线性基没有异或和为\(0\)的子集.
- 线性基中的元素异或出来的值是不一样的.
- 线性基中每个元素的最高二进制位不同.
构造方法:
我们设线性基为集合\(A\),\(A_k\)表示最高位为\(k\)的元素值.
我们每次往线性基内插入一个数\(x\),我们从高位到低位一位一位贪.
如果\(x\)在\(k\)位上是\(1\).
1. 如果存在\(A_k\),那么我们将\(x \ xor \ A_k\).
2. 如果不存在\(A_k\),那么我们将\(A_k=x\).
bool insert(LL x) {
if(!x)return true;
for(int i=60; ~i; --i) {
if(x&(1LL<<i)) {
if(!p[i]) {
p[i]=x;++cnt;
return false;
}x^=p[i];
if(!x)return true;
}
}
}
注: 返回值表示能不能异或出\(0\).
性质四 prove
假如存在一个集合\(B=[A_i,A_j,A_k]\)异或起来的值等于\(C=[A_x,A_y,A_z]\).令它们两两不同,那么我们可以得到\(A_i\ xor A_j\ xor A_k=A_x\ xor A_y\ xor A_z\).我们令\(A_x\)是这些元素中最大的,显然由于线性基的性质五,不可能有跟它异或起来为\(0\)的值.那么两边注定不能相等.
注: 其它几个性质比较好证,这里就不解释了.
补充:
1.选择任意元素异或起来与x的异或最大值
思路:
我们从最高位往最低位贪心,如果\(Ans\)第\(k\)位上为\(0\),那么我们将\(Ans\ xor \ A_k\),否则不进行任何操作.
核心代码:
LL qur_mx(LL x) {//求解选取任意元素异或起来与x的异或最大值.
LL res=x;
for(int i=60; ~i; --i)if(!(res&(1LL<<i)))res^=p[i];
return res;
}
2.查询异或第\(k\)小
思路:
简单来说就是将线性基重建,使得它们之间不会互相干扰,然后我们可以写个类似线段树二分一样的东西.(具体看代码)
核心代码:
void rebuild() {
for(int i=60; ~i; --i)
for(int j=i-1; ~j; --j)
if(p[i]&(1LL<<j))p[i]^=p[j];
cnt=0;
for(int i=0; i<=60; ++i)if(p[i])d[++cnt]=p[i];
}
LL kth_Mi(LL k) {
if(Zero)--k;
if(k>=(1LL<<cnt))return -1;
LL res=0;
for(int i=cnt; i; --i)
if(k&(1LL<<i))res^=d[i];
return res;
}
3.查询排名
思路:
大概就是将线性基重建后求解.
核心代码:
LL rank(LL x) {
LL rk=1;
for(int i=cnt; i; --i)
if(x>=d[i])x^=d[i],rk+=1LL<<i-1;
return rk;
}
线性基的应用
P3812 【模板】线性基
思路:板子题.
P3265 [JLOI2015]装备购买
思路:
数量最多其实就是线性基内的元素个数.
花费最小,我们可以先按照每个装备的花费排序,从小的选起.装备的属性是一个向量,线性基里的元素其实就是一个向量.我们用类似高斯消元的方法就好了.
P4301 [CQOI2013] 新Nim游戏
思路:
对于传统的\(nim\)游戏,如果所有元素的\(xor\)和为\(0\),那么先手必败,否则先手必胜.
再看这道题,当第一个人拿走了若干堆,第二个人的目的就是选出若干个元素使得它们的异或和为\(0\).所以对于第一个人来说,拿掉的应该是不能插入线性基内的元素.我们可以先按数从大到小排序,这样能保证第一回合所取最小.
P4570 [BJWC2011]元素
思路:
这道题类似上面一题.
P4151 [WC2011]最大XOR和路径
思路:
我们可以将一条路径拆成两部分,一部分是环,另一部分是随便一条\(1->n\)的链.
因为我们可以发现对于连接一个环和一条链的边会被遍历两次,对答案没有影响.所以我们可以将每个环丢到线性基里,最后线性基内查询与\(dis[1,n]\ xor\)最大的值.
CF845G Shortest Path Problem?
思路:
与上一道题一模一样.
CF724G Xor-matic Number of the Graph
思路:
我们同样将一条路径拆成两部分.
由于我们直接枚举两个点会超时,考虑计算每一位的贡献.假设当前这位为\(k\).
如果有一个环的第\(k\)位为\(1\),那么显然我们任意取两个点都存在\(2^{S-1}\)条路径第\(k\)位为\(1\),\(S\)表示线性基的大小.因为我们异或上这个环第\(k\)位上的数就会发生变化.那么\(1\)和\(0\)的概率是相等的.答案就是\(2^{S-1}*2^{k}*(sz*(sz-1)/2)\),\(sz\)表示点数.
如果没有一个环的第\(k\)位为\(1\),那么两个点\(x,y\)它们之间的\(dis=dis[x]\ xor \ dis[y]\),那么我们需要强制其中一个为\(1\),另一个为\(0\).答案就是\(2^k*2^{S}*cnt*(sz-cnt)\).
CF895C Square Subsets
思路:
我们显然可以将每个二进制位表示每个质数,那么是一个平方数就相当于\(xor\)是\(0\).(但是我写了状压dp).
P4869 albus就是要第一个出场
思路:
首先先说一个结论:如果线性基内的元素有\(S\)个,那么每个能被异或出来的数的异或方案数相同,且为\(2^{n-S}\).
这个结论想想就是对的,懒得证了.剩下我们只用求一个排名即可.
来个板子(不清楚对不对)
struct XXJ {
int cnt;
LL p[100],d[100];
bool Zero;
void Clear() {
Zero=cnt=0;
for(int i=0; i<60; ++i)d[i]=p[i]=0;
}
bool insert(LL x) {
if(!x)return true;
for(int i=60; ~i; --i) {
if(x&(1LL<<i)) {
if(!p[i]) {
p[i]=x;++cnt;
return false;
}x^=p[i];
if(!x)return true;
}
}
}
LL qur_mx(LL x) {
LL res=x;
for(int i=60; ~i; --i)if(!(res&(1LL<<i)))res^=p[i];
return res;
}
void rebuild() {
for(int i=60; ~i; --i)
for(int j=i-1; ~j; --j)
if(p[i]&(1LL<<j))p[i]^=p[j];
cnt=0;
for(int i=0; i<=60; ++i)if(p[i])d[++cnt]=p[i];
}
LL kth_Mi(LL k) {
if(Zero)--k;
if(k>=(1LL<<cnt))return -1;
LL res=0;
for(int i=cnt; i; --i)
if(k&(1LL<<i))res^=d[i];
return res;
}
LL kth_Mx(LL k) {
return kth_Mi((1LL<<cnt)-k+1);
}
LL rank(LL x) {
LL rk=1;
for(int i=cnt; i; --i)
if(x>=d[i])x^=d[i],rk+=1LL<<i-1;
return rk;
}
}LB;

浙公网安备 33010602011771号