Loading

[笔记] 线性基

用处

  • 快速查询一个数是否可以被一堆数异或出来
  • 快速查询一堆数可以异或出来的最大 / 最小值
  • 快速查询一堆数可以异或出来的第 \(k\) 大值

性质

  • 原数列里的任何一个数都可以通过线性基里的数异或表示出来
  • 线性基里任意一个子集的异或和都不为 \(0\)
  • 一个数列可能有多个线性基,但是线性基里数的数量一定唯一,而且是满足性质一的基础上最少的。

实现

构造
void insert(LL x){
	rfor(i, lim, 0) if(x & (1LL << i)){
		if(a[i]) x ^= a[i];
		else{ a[i] = x; break; }
	}
}
查询一个元素是否可以被异或出来
bool check(LL x) {
	rfor(i, lim, 0) if(x & (1LL << i)) x ^= a[i];
	return x == 0;
}
查询异或最大值

按位贪心.

LL askmax() {
	LL ans = 0;
	rfor(i, lim, 0) if(ans < (ans ^ a[i])) ans ^= a[i];
	return ans;
}
查询异或最小值

一般来说就是线性基里的最小元素,特判 \(0\).

LL askmin() {
	if(zero) return 0;
	lfor(i, 0, lim) if(a[i]) return a[i];
}
查询异或第 \(k\)

重建线性基,使得不同位尽量不相互影响。

void rebuild() {
	rfor(i, lim, 0) rfor(j, i - 1, 0) if(a[i] & (1LL << j)) a[i] ^= a[j];
	lfor(i, 0, lim) if(a[i]) p[++cnt] = a[i];
}

类似树状数组二分第 \(k\) 小,同样特判 \(0\).

LL kth(int k) {
	LL ans=0;
	rfor(i, lim, 0) if(k & (1LL << i)) ans ^= p[i];
	return ans;
}
查询排名

与查询第 \(k\) 小类似.

int rank(LL x) {
	LL ans = 0;
	rfor(i, cnt - 1, 0) if(x >= p[i]) ans += (1LL << i), x ^= p[i];
	return ans + zero; 
}

题目

[WC2011]最大XOR和路径

\(1-n\) 的最大XOR和路径可以看作任意一条从 \(1-n\) 的路径异或上若干个环。

[CQOI2013]新Nim游戏

分析出答案的补集就是线性基。


posted @ 2022-02-16 15:20  IrisT  阅读(50)  评论(0编辑  收藏  举报