线性基

线性基

1、三大性质:

  • 原序列里面的任意一个数都可以由线性基里面的一些数异或所得到
  • 线性基里面的任意一些数异或起来都不可能得到 \(0\)
  • 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

2、构造 \(p\) 数组:每次我们插入一个数 \(x\) 时,如果 \(x\) 二进制的第 \(i\)\(1\),且 \(d[i]==0\),则表示此位还没有存入数字进去,则在第 \(i\) 位插入数字 \(x\);否则,则让 \(x=x\ xor\ d[i]\),继续往低位遍历。

struct Basis{
	static const int N = 60;
	i64 p[N + 1], p_[N + 1];
	i64 tot;
	bool is_0;
	Basis() {
		memset(p, 0, sizeof p);
		memset(p_, 0, sizeof p_);
		is_0 = 0;
		tot = 0;
	}

	inline void add(i64 x) {// 添加某个数进去
		for (i64 i = N; i >= 0; i--) {
			if (x >> i & 1) {
				// 如果这位之前已经被其他数所占用了,那就不能将这个数加进去,只能继续往低位找
				if (p[i]) {
					x ^= p[i];
				} else {
					tot++;
					p[i] = x;
					break;
				}
			}
		}
		if (x == 0) is_0 = 1;
	}

	inline bool is(i64 x) {
		// 判断这个数是否存在
		if (!x) {
			// 0需要直接特判是否存在
			if (is_0) return true;
			return false;
		}
		for (i64 i = N; i >= 0; i--) {
			if (x >> i & 1) {
				if (!p[i]) return false;
				x ^= p[i];
			}
		}
		return true;
	}

	inline i64 get_max() {// 得到这个这个线性基内任意取数异或所产生的最大值
		i64 ans = 0;
		for (i64 i = N; i >= 0; i--) {
			ans = max(ans, ans ^ p[i]);
		}
		return ans;
	}

	inline i64 get_min() {// 得到这个这个线性基内任意取数异或所产生的最小值
		if (is_0) return 0LL;
		for (i64 i = 0; i <= N; i++) {
			if (p[i]) return p[i];
		}
	}

	inline void work() {// 预处理
		for (i64 i = 0; i <= N; i++) p_[i] = p[i];
		for (i64 i = 1; i <= N; i++) {
			for (i64 j = 0; j < i; j++) {
				if (p_[i] >> j & 1) {
					p_[i] ^= p_[j];
				}
			}
		}
	}

	// 求序列中选取任意数的第k小的数
	inline i64 kth_min(i64 k) {
		i64 total = 1LL << tot;
		if (k == 1 && is_0) return 0;
		if (is_0) k--, total--;
		if (k > total) {// 表示序列没有超过k个不同的异或和值
			return -1;
		}
		work();
		i64 ans = 0;
		for (i64 i = 0; i <= N; i++) {
			if (p_[i]) {
				if (k & 1) ans ^= p_[i];
				k >>= 1LL;
			}
		}
		return ans;
	}

	// 求序列中选取任意数的第k大的数
	inline i64 kth_max(i64 k) {
		i64 tal = 1LL << tot;
		if (!is_0) tal--;
		k = tal - k + 1;
		if (k <= 0) {
			return -1;
		}
		return kth_min(k);
	}
};

3、模版一:洛谷P3812

void solve() {
    int n;
    cin >> n;
    vector<i64> a(n + 1);
    Basis t;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        t.add(a[i]);
    }
    cout << t.get_max() << '\n';
}

4、模版二:loj

void solve() {
	int n;
	cin >> n;
	vector<i64> a(n + 1);
	Basis t;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		t.add(a[i]);
	}
	int m;
	cin >> m;
	for (int i = 1; i <= m; i++) {
		i64 k;
		cin >> k;
		cout << t.kth_min(k) << '\n';
	}
}

5、提升题:洛谷P4570洛谷P3292洛谷P3857

posted @ 2024-08-31 22:06  grape_king  阅读(24)  评论(0)    收藏  举报