线性基

导引问题:给出 nn 个数: a1,a2,...,ana_1, a_2, ..., a_n ,请问:
取其中的一些数进行异或操作,能否得到值 QxQ_x ?

# 前置知识:异或运算

# 性质

相异为 11,相同为 00
如果 c=abc=a\oplus b
则有: ac=ba\oplus c=bbc=ab\oplus c=a

小问题

1.1. 给出 2n+12n + 1 个整数,除了 11 个数外,其余 2n2n 个数均成对出现,请编程找出这个单独的数

Answer

累异或,成对的都消掉了

2.2.n+1n + 1 个数组成的数列,包含 1n1\to nnn 种整数,也就是说,有一个数是重复出现的,求其值

Answer

解:异或整个数列异或( 1n1\to n )

3.3. 给出 2n+22n + 2 个整数,除 22 个数外,其余 2n2n 个数均成对出现,清编程找出这 22 个不成对的数

Answer

解:做两遍异或,两个单独的数将整个数列分为两大类

# 线性基

# 定义

对于一个数集 {A}\{A\},它的线性基是一个最小的数集 {B}\{B\},使得A中任意一个数可以通过 {B}\{B\} 中的一些数异或得到

对于一个数集,他可能存在不止一个线性基

在某些涉及异或都操作中,线性基可以代替原集合,从而达到减小数据规模的目的

# 举例

对于一个集合 A{4,5,6}A:\{4, 5, 6\}
它的线性基可以是:
1.{6,1,2}1.\{6, 1, 2\}
2.{4,1,2}2.\{4, 1, 2\}
3.{5,1,3}3.\{5, 1, 3\}
\dots

# 如何求线性基

通过线性代数的知识,求出极大线性无关组

A:{4,5,6}{1102,1012,1002}10021002,100211020102,101210020012.B:{1002,010x,0012}{4,2,1}\because A:\;\{4, 5, 6\}\Longrightarrow \{110_2, 101_2, 100_2\}\\ 100_2\leftrightarrow100_2,\\100_2 \oplus 110_2\leftrightarrow010_2,\\101_2\oplus100_2\leftrightarrow001_2.\\ \therefore B:\;\{100_2,\;010_x,\;001_2\}\Longrightarrow\{4, 2, 1\}

线性基的关键在于降低规模
X=ai1ai2...aikX=a_{i1}\oplus a_{i2}\oplus ...\oplus a_{ik} ,则意味着 XX 可被其他已选的数表示,所以就去掉 XX (不把 XX 当作线性基元素)

思考:为什么要是求得的线性基最高位不同

Answer

便于计算出 XX 是否能被现有的数表示。

e.g.

问:1241、2、4 线性基能否表示 77
由于
100121\rightarrow001_2
201022\rightarrow010_2
410024\rightarrow100_2
每一位来看,异或起来可以变成 1112=7111_2=7

线性基的形式:
a[1]=1XXXXXa[1]=1XXXXX
a[2]=01XXXXa[2]=01XXXX
a[3]=0001XXa[3]=0001XX
\dots
为了方便,设置d[i]表示非0最高位为右数第i位的数
d[5]=1XXXXXd[5]=1XXXXX
d[4]=01XXXXd[4]=01XXXX
d[2]=0001XXd[2]=0001XX

// 对于 a[1].a[2].....a[n] 的线性基
ll d[61];
for ( int i = 1; i <= n; i ++ ) { // 枚举这个序列
	ll x = a[i];
	for ( int j = 60; j >= 0; j -- ) {  // 从高位向下看看还需不需要再构造东西了
		if ( x & (1ll << j) ) { // 如果a[i]这一位是"1"
			if ( !d[j] ) { d[j] = x; break; } // 如果d[j]也没有构造过就构造一下
			else         x ^= d[j];           // 否则通过异或将这一位去掉
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11

# 线性基的性质

线性基没有值为 00 的元素 ( 因为 00 在异或中不起作用 )
线性基元素中任意一个都无法通过另外的线性基元素异或得到
原集合中任意一个都可以通过线性基中某些数异或得到

# 问题解决

# Question_1Question\_1

回看导引问题

Answer

将这n个数求出线性基,将询问的值通过线性基后为0则表示可以表示,否则无法表示

inline bool check ( ll x ) {
	for ( int j = 60; j >= 0; j -- ) {
		if ( x & (1ll << j) ) {
			if ( !d[j] ) return false;
			else x ^= d[j];
		}
	}
	return (x == 0);
}
1
2
3
4
5
6
7
8
9

# Question_2Question\_2

给出 nn 个数,a[1],a[2]...a[n]a[1],a[2]...a[n] ,问取出其中一些数进行异或操作,能够得到的最大的数是多少

Answer

用最简形式的线性基异或
1.将这 nn 个数求出线性基 d[]d[]
2.将答案初始化为 00
3.从最高位向最低位查询,若当前答案该位(第ii位)为 00d[i]0d[i]\neq 0,则当前答案异或 d[i]d[i]
4.所有位查询以后得到的答案就是最大值

inline ll Find ( ) {
	ll res = 0;
	for ( int i = 60; i >= 0; i -- ) {
		if ( ((1ll << i) & res) == 0 && d[i] ) res ^= d[i]; // 该位相反为1
	}
	return res;
}
1
2
3
4
5
6
7

# Question_3Question\_3

给出 nn 个数,a[1],a[2]...a[n]a[1],a[2]...a[n] ,问取出其中一些数进行异或操作,能够得到的最小的数是多少

Answer

将这n个数求出线性基 d[]d[]
在构造线性基的过程中,若存在一个数插入线性基时插入失败,即插入后 x=0x=0 ,那么最小值为 00
若答案不为 00,则答案为最小可确定位所表示的 d[i]d[i] ,即:从低位往高位看,如果一个位置的 d[i]0d[i]\neq0,则答案就是该 d[i]d[i]

# Question_4Question\_4

给出 nn 个数,a[1],a[2],...,a[n]a[1],a[2],...,a[n],问取出其中的一些数进行异或操作,能够得到的kk 小的数是多少(去重过)

Answer

求出最简线性基 d[]d[]
观察 d[]d[] ,我们只能在 d[i]0d[i]\neq0 时自由选择第 ii 位为 00 或者为 11
由于线性基无法异或出 00 ,特盘是否会出现异或为 00 的情况,若出现则 k1k-1
对于能够选择的位,我吗可以确定 00 或者 11 。那我们将 kk 二进制表示,若某一位为 00 ,则可以选择位的值置为 00,否则置为 11

这里可以采用k的二进制去求:
我们从 dd 下标 00 开始往上照到第一个不为 00 的线性基
如果 kk 这一位是 11 ,就把找到的对应线性基异或进答案,否则就不异或进去
然后 kk 整除 22 ,继续这一过程

Last Updated: 10/14/2023, 7:51:49 PM