线性基

线性基

本文在学习了博客线性基学习笔记,总结自己的一些理解

处理的问题

对于一组数,通过构造一组线性无关的基底来求解异或最值、异或第k小值以及某值是否可得到的问题。

构造方法

维护一组线性基:对于每一个给定的数,对其二进制表示,从高位到低位扫描,当发现第x位是1时,若当前最高位为x的基底(即bs[x])为空,则bs[x]置为该数,否则将该数异或上bs[x],继续往下扫描。

ll bs[70];
void insert(ll x)
{
	for (int i=62;i>=0;--i)
	{
		if (x&(1ll<<i))
		{
			if (!bs[i])
			{
				bs[i]=x;
				return;
			}
			x^=bs[i];
		}
	}
}

原理

对于给出的数,将其二进制表示看成一个向量,对这一组向量,按上述方法,构造出一组线性无关基底,满足下列性质: * 最高位1的位置互不相同 * 该组基底能异或出的值的集合,与原来的那组数能异或出的值的集合,相同 * 任意一个可以用这些向量组合出的向量x,组合方式唯一 对于性质一,容易看出按照上述方法可以构造出来。 对于性质二,这是后面用来求解异或最值等问题的正确性的保证。证明这个性质的大概思想:令c=a xor b,则a和c可以得到b,则c可以代替b,加入本该是a、b的组中。 对于性质三,参考开头提到博客的证明方法: 假设x的组合方法不唯一, 也就是说 x=a1 xor a2 ⋯ ap = b1 xor b2 ⋯ bq 那么x xor x = 0 = a1 xor a2 ⋯ ap xor b1 xor b2 ⋯ bq 也就是说 可以用这个向量组里的向量组合出0向量, 与线性无关矛盾。 故组合方法唯一。 ### 应用 #### 求解一组数异或最值问题 求最大值:从最高位开始,若异或上该基底可以使当前值变大,则异或。做到最低位结束,得到即为答案 ```C++ ll max_qry(ll init=0) // init为初始值,若没有,则置为0; { for (int i=62;i>=0;--i) { if ((init^bs[i]) 求最小值:从低位往上找到的第一个非零基底。这里特别提一下,若有初始值,则应该仿照上述求最大值的方法,从**高位**开始贪心异或,能使其变小就异或上。 ```C++ ll min_qry(ll init=0) { if (init==0) { for (int i=0;i<=62;++i) if (bs[i]) return bs[i]; } else { for (int i=62;i>=0;--i) if ((init^bs[i]) 从高位向低位扫这个数的二进制表示,碰到值为1的位,则异或上最高位为这个位的基底。若最后该数变为0,则表示该数可以被这组基的子集构造出来。结合性质三感受一下。 ```C++ bool check(ll x) { for (int i=62;i>=0;--i) if (x&(1ll< 在原有线性基的基础上,重新构造一组基底。这组基底相比于原来的,每个基底变为通过异或得到的包含原来最高位的最小值。再对k二进制表示,异或上对应位的新基底即可。这里特别提一下,0的情况。如果构造基底的个数少于题目给定的数的个数,说明0使可以被异或出来的。那么对于k,要使k=k-1。 方法如下 ```C++ vector tmp; ll kth_query(int k) { for(int i=0;i<=62;++i) { if (!bs[i]) continue; for(int j=i-1;j>=0;--j) if(bs[i]&(1ll<=(1<
原文地址:https://www.cnblogs.com/orangee/p/10159717.html