线性基学习笔记
补坑
线性基在我的理解来看,就是把原来较大的集合转换成最大的数的二进制位数大小的集合。通过这个操作可以用选择子集异或的形式表示出原集合。
基础实现
每次插入一个数就从高位到低位,如果这位是1的话,分两种情况。如果线性基里存在这一位了,那么这个数异或上线性基里的数,不然的话线性基里这一位数就是这个数啦。
代码实现很简单。
void build(ll x)
{
for(int i=lg-1;i>=0;i--)
{
if(x&(1ll<<i))
{
if(ext[i]) x^=s[i];
else{s[i]=x,ext[i]=1;break;}
}
}
}
写丑了嘤嘤嘤
维护对角矩阵
把线性基看成一个矩阵,利用类高斯消元法的方法维护。这就是是一个矩阵且每一列至多有1个1,且这些1一定是在上三角中。这个很明显,因为第k位上的1一定只会出现在前k行而一定不会出现在后面,所以满足一个上三角矩阵。
实现其实很简单啦,就是通过第i行下面的消自己,再用第i行去消上面。
【听大佬说这玩意没用。。。】
for(int i=lg-1;i>=0;i--)
{
if(x&(1ll<<i))
{
if(ext[i]) x^=s[i];
else
{
s[i]=x,ext[i]=1;
for(int j=i-1;j>=0;j--) if(s[j]&&s[i]&(1<<j)) s[i]^=s[j];
for(int j=i+1;j<lg;j++) if(s[j]&&s[j]&(1<<i)) s[j]^=s[i];
break;
}
}
}
题目
洛谷模板
求异或最大值。
显而易见,肯定要根据原集合建线性基。然后贪心的从高位到低位选择。如果答案异或上当前这个数可以变大的话就异或上。
这个的正确性是因为对于第k位,只有前k个数可能能影响到这一位。如果现在在第k位且异或上答案变大(0->1),那么接下来的数异或一定不会影响这一位。这个思想其实类似字典序?
洛谷【P3857】[TJOI2008]彩灯
题意:有n个彩灯,一些开关,每个开关可以控制某些彩灯。问有几种不同的样式。
分析一下。每个灯代表1位的话,那么每个开关对应一个数,求异或后有多少种不同的取值。那么直接扔进线性基,看有多少位是可以异或的,那么最后的答案就是2^k k表示线性基中数的个数。
这个比较好理解,就是这些数可以控制这一位0/1,所以最后的答案就是这k位各取0,1的方案数。
洛谷P4151 [WC2011]最大XOR和路径
题意:找一条路径,要求异或和最大。
我不会做。看的题解。
大概是这个样子。对于一条边如果往返走,它对答案没有贡献。(异或为0)也就是说我们现在有一条1-n的简单路径,然后如果我们想要异或其他值的话,只有可能异或上环。因为如果不是环的话一定是走了两遍。所以把所有环扔进线性基,然后最后把简单路径作为起始值,求异或最大值就行啦。
问题来了,如何选择这个简单路径。
其实选择哪一条都行,因为如果我们当前的路径不是最优的话,另一条路径一定和当前路径构成环。(都是从1-n,所以一定构成环)那么我们放在线性基里的时候异或上这个环的话,我们的简单路径实际上就变成了那个更优的。
如下图。红色更优,那么我们异或上中间的环,就从红色变成蓝色了。
代码实现很简单就跑个dfs就ok。
void dfs(int x)
{
vis[x]=1;
for(int i=in[x];i;i=e[i].lt)
{
int v=e[i].to;
if(vis[v]) insert(dis[x]^dis[v]^e[i].v);
else dis[v]=dis[x]^e[i].v,dfs(v);
}
}
线性基还是比较好理解的一个东西[只要别带着那些概念/捂脸]
以后再补题吧qwq
10.11update
溺死在初赛的hywn冒个泡
见到一道比较有意思的题qwq
给定一个集合,求每次删除一个元素的异或最大值。
首先,线性基不资瓷删除。所以直接做是不可楞滴。
我们发现每次只删除一个元素,诶嘿嘿,我们直接对于前缀记录线性基,后缀记录线性基,然后暴力合并/xyx
时间复杂度O(nlg^2n)空间O(nlgn)
所以这个题其实可以拓展到区间一样做qwq