线性基

转自我的洛谷博客。

线性基是用于处理多数异或的工具。

构造

每次插入一个数,如果一个位置他有,但基底没有的位置,那么插入他。

否则废掉这一位。

void insert(int x)
{
    for(int i=M-1;i>=0;i--)
    {
        int v=(x>>i)&1;
        if(!v) continue;
        if(!B[i]) 
        {
            B[i]=x;
            return;
        }
        x^=B[i];
    }
    flag=true;
}

其中 flag 就表示该数能否被之前的基底凑出来。

求集合中任选数的异或 \(max、min\)

  • Max

从高到低位便利,如果能使答案更优就异或,否则就不异或。

int Max()
{
	int res=0;
	for(int i=M-1;i>=0;i--)
	{
		res=max(res,res^B[i]); 
	}
}
  • Min

从低到高,如果有就选。

int Min()
{
	for(int i=0;i<M;i++) if(B[i]) return B[i];
}

判断一个数是否出现过

类似插入,如果插不进去就是能被凑出来。

bool check(int x)
{
	for(int i=M;i>=0;i--)
	{
		int v=(x>>i)&1;
		if(!v) continue;
		if(!B[i]) return false;
		x^=B[i];
	}
	return true;
}

以上二者的结合:

对二元组 \((x,y)\) 集合询问,满足 \(\oplus x=A\) 的情况下,最大化 \(\oplus y\)

将二元组左移压缩为一个数,对第二关键字按位贪心。

设当前答案为 \(cur\),每次查 \(A\times 2^{30}+(cur+2^j)\) 能否被线性基异或出来,如果可以这一位就是 \(1\),否则为 \(0\)

[ABC249G] Xor Cards

\(i\) 位能否位 \(1\)

设线性基大小为 \(sz\)

线性基上有的位一定可以为 \(1\),且恰有 \(2^{sz-1}\) 个数里面有这一位,同理有这么多没有。

线性基上没有的位得看别人脸色,如果某些位上带了它,那就可以有这一位,和上面的情况就一样了,否则全部 \(2^sz\) 个数都没有这一位。

注意如果必须要选的话需要查看 \(0\) 是否能被异或出来。

Xor-matic Number of the Graph

查询异或集合第 \(k\)

  • 去重后第 \(k\)

首先判断能否凑出 0,看 flag 即可。

将基底消成上三角。

消成上三角的目的是保证异或这一位的线性基就是选上这一位。

然后将 \(k\) 做二进制分解。

int query(int k)
{
    if(flag) k--;
    if(!k) return 0;
    int cnt=0,res=0;
    for(int i=0;i<M;i++)
    {
        for(int j=i-1;j>=0;j--)
        {
            if((B[i]>>j)&1) B[i]^=B[j];
        }
        if(B[i]) tmp[cnt++]=B[i];
    }
    if(k>=(1ll<<cnt)) return -1;
    for(int i=0;i<cnt;i++)
    {
        if((k>>i)&1) res^=tmp[i];
    }
    return res;
}
  • 不去重第 \(k\)

重要结论:线性基中每个可以被异或出来的数可以由 \(2^{n-|B|}\) 种不同组合异或出来

假设想凑出 \(x\),那些没有插入到线性基中的元素,任选一个子集做异或和,然后线性基凑出这个和,再用线性基内的元素凑出想凑出的 \(x\)

那么我们可以通过给出的 \(k\) 找到去重后的 \(k'\),再用上面的方法做就行了。

如果想找到线性基中能凑出多少个比某个数 \(x\) 小的数,可以先找出线性基中有的位,然后对 \(x\) 做二进制分解,如果 \(x\) 的第 \(j\) 位是 \(1\) 而且线性基中有第 \(j\) 位,那么不选 \(j\) 的所有方案一定都比他小。

P4869 albus就是要第一个出场

前缀线性基:

类似扫描线,将线性基的基底尽可能地往后选。

插入的时候遇到之前有的交换插入即可。

void insert(int x,int c)//位置x,数值c
{
	for(int i=M-1;i>=0;i--)
	{
		int v=(c>>i)&1;
		if(!v) continue;
		if(!p[i]) 
		{
			p[i]=c;
			pos[i]=x;
			break;
		}
		else if(pos[i]<x) swap(pos[i],x),swap(p[i],c);
		c^=p[i];
	}
} 

P3292 [SCOI2016] 幸运数字

线性基合并:

将一个的基底全部取出,然后插入另一个。

复杂度 \(log^2V\)

“特征值”

一个数不能被某一线性基表示出来的部分,定义为特征值。

他和线性基异或出的最小值就是特征值,可以理解为在选线性基的某些位的异或和都要加上这个特征值。

在某个线性基上,两个数本质不同即特征值不同。

应用:求一个集合 \(S\) 的每个元素 \(a_i\) 和一个线性基 \(B\) 异或出来的集合的并的数量,可以表示为 \(S 中不同特征值的数量\) \(\times\) \(2^{|B|} -1\).

Beijing2011 梦想封印

实数线性基

给出 \(n\)\(m\) 维向量,求实数线性基

方法其实和二进制线性基相同。

在异或线性基中用异或消掉非线性基中向量的某位,那么这里同样可以用向量加减的方法操作。

bool insert(Vd Z)
{
    for(int j=m-1;j>=0;j--)
    {
        if(abs(Z[j])<eps) continue;
        if(abs(B[j][j])<eps) 
        {
            B[j]=Z;
            return true;
        }
        double t=Z[j]/B[j][j];
        Z=Z-(B[j]*t);
    }
    return false;
}

P3265 [JLOI2015] 装备购买

图上路径异或和

图上路径异或和可以看作起点到终点的任意一条链的异或和与任选的和起点联通的几个环的异或和。

P4151 [WC2011] 最大XOR和路径

集合完全平方数

首先 \(\sqrt V\) 以内的质因子插入线性基,大于 \(\sqrt V\) 的质因子每个数只有一个,把线性基中的位按照这个分开。

前面的位每个数最多只有一个 \(1\),找到这个 \(1\) 的位是否已经插入了即可快速处理这部分。

后面的位用正常线性基处理即可。

posted @ 2025-05-09 11:55  Richard_whr  阅读(19)  评论(0)    收藏  举报