线性基学习笔记

补坑

线性基在我的理解来看,就是把原来较大的集合转换成最大的数的二进制位数大小的集合。通过这个操作可以用选择子集异或的形式表示出原集合。

基础实现

每次插入一个数就从高位到低位,如果这位是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

原文地址:https://www.cnblogs.com/hanyuweining/p/10321982.html