【算法笔记】线性基

1|0线性基


定义:给定数集s,以异或运算张成的数集与S相同的极大线性无关集,称为原数集的一个线性基。

性质

  1. 原数集的任意一个数都能有线性基内部的一些数异或得到。
  2. 线性基内部任意数异或不为 0
  3. 线性基内数唯一,且保证性质一的情况下,数的个数最少。
  4. 线性基内每个数的最高有效位各不相同。

如何判断原数集能否异或出0,在插入的过程中如果插入失败就可以异或出0

struct Basis { vector<ui64> B; bool zero, sorted; Basis() { B = vector<ui64>(); zero = false, sorted = true; } void sort() { if (sorted) return; sorted = true; ranges::sort(B); return; } void insert(ui64 x) { for (auto b: B) x = min(x, b ^ x); if (x == 0) { zero = true; return; } for (auto &b: B) b = min(b, b ^ x); B.push_back(x), sorted = false; return; } int query(int kth = 1) { // query k-th smallest element sort(); if (zero) kth--; ui64 ans = 0; for (auto b: B) { if (kth & 1) ans ^= b; kth >>= 1; } if (kth == 0) return ans; return -1; } ui64 max() { ui64 ans = 0; for (auto b: B) ans ^= b; return ans; } bool check(ui64 x) { for (auto b: B) x = min(x, b ^ x); return x == 0; } void merge(const Basis &oth) { for (auto x: oth.B) insert(x); return; } };

2|0例题


2|1P3812 【模板】线性基


验证模板,考虑到线性基内数的最高有效位各不相同,所以直接把所有的数异或就是答案

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui64 = uint64_t; struct Basis { vector<ui64> B; bool zero, sorted; Basis() { B = vector<ui64>(); zero = false, sorted = true; } void sort() { if (sorted) return; sorted = true; ranges::sort(B); return; } void insert(ui64 x) { for (auto b: B) x = min(x, b ^ x); if (x == 0) { zero = true; return; } for (auto &b: B) b = min(b, b ^ x); B.push_back(x), sorted = false; return; } int query(int kth = 1) { // query k-th smallest element sort(); if (zero) kth--; ui64 ans = 0; for (auto b: B) { if (kth & 1) ans ^= b; kth >>= 1; } if (kth == 0) return ans; return -1; } ui64 max() { ui64 ans = 0; for (auto b: B) ans ^= b; return ans; } bool check(ui64 x) { for (auto b: B) x = min(x, b ^ x); return x == 0; } void merge(const Basis &oth) { for (auto x: oth.B) insert(x); return; } }; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); Basis b; int n; cin >> n; for (int i = 1; i <= n; i++) { ui64 x; cin >> x; b.insert(x); } cout << b.max(); return 0; }

2|2P3857 [TJOI2008] 彩灯


线性基的元素有x个,每一个都有选或不选两种,方案数2x

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui64 = uint64_t; struct Basis { vector<ui64> B; bool zero, sorted; Basis() { B = vector<ui64>(); zero = false, sorted = true; } void sort() { if (sorted) return; sorted = true; ranges::sort(B); return; } void insert(ui64 x) { for (auto b: B) x = min(x, b ^ x); if (x == 0) { zero = true; return; } for (auto &b: B) b = min(b, b ^ x); B.push_back(x), sorted = false; return; } int query(int kth = 1) { // query k-th smallest element sort(); if (zero) kth--; ui64 ans = 0; for (auto b: B) { if (kth & 1) ans ^= b; kth >>= 1; } if (kth == 0) return ans; return -1; } ui64 max() { ui64 ans = 0; for (auto b: B) ans ^= b; return ans; } bool check(ui64 x) { for (auto b: B) x = min(x, b ^ x); return x == 0; } void merge(const Basis &oth) { for (auto x: oth.B) insert(x); return; } }; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); Basis b; int n, m; cin >> n >> m; for (int i = 0; i < m; i++) { string s; cin >> s; i64 val = 0; for (auto c: s) { val <<= 1; val |= (c == 'O'); } b.insert(val); } int t = b.B.size(), res = 1; for (int i = 0; i < t; i++) res = res * 2 % 2008; cout << res; return 0; }

2|3P4570 [BJWC2011] 元素


首先选出的集合内不能存在异或和为0的情况,这个可以用线性基来维护。

值最大可以贪心,优先插入魔力值大的。假设集合内存在abcd=0的情况,我们肯定扔掉最小值最优。

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui64 = uint64_t; struct Basis { vector<ui64> B; bool zero, sorted; Basis() { B = vector<ui64>(); zero = false, sorted = true; } void sort() { if (sorted) return; sorted = true; ranges::sort(B); return; } void insert(ui64 x) { for (auto b: B) x = min(x, b ^ x); if (x == 0) { zero = true; return; } for (auto &b: B) b = min(b, b ^ x); B.push_back(x), sorted = false; return; } int query(int kth = 1) { // query k-th smallest element sort(); if (zero) kth--; ui64 ans = 0; for (auto b: B) { if (kth & 1) ans ^= b; kth >>= 1; } if (kth == 0) return ans; return -1; } ui64 max() { ui64 ans = 0; for (auto b: B) ans ^= b; return ans; } bool check(ui64 x) { for (auto b: B) x = min(x, b ^ x); return x == 0; } void merge(const Basis &oth) { for (auto x: oth.B) insert(x); return; } }; using vi = vector<i64>; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; vi number(n), magic(n), p(n); for (int i = 0; i < n; i++) cin >> number[i] >> magic[i], p[i] = i; ranges::sort(p, [&](const int x, const int y) { return magic[x] > magic[y]; }); Basis b; i64 res = 0; for (auto i: p) { if (b.check(number[i])) continue; b.insert(number[i]), res += magic[i]; } cout << res; return 0; }

2|4P4301 [CQOI2013] 新Nim游戏


首先根据Nim游戏的性质,我们知道如果初始的异或和不为0,先手必胜。

假设在第一轮先手操作后异或和不为 0,后手是不可能通过拿操作是的异或为 0 的。对于先手来说,极端情况就是保留一个,此时异或和一定不为0。所以先手必胜。

先手选取的方式用线性基来维护,总数最小参考上一题的贪心处理就好了。

#include<bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using ui64 = uint64_t; struct Basis { vector<ui64> B; bool zero, sorted; Basis() { B = vector<ui64>(); zero = false, sorted = true; } void sort() { if (sorted) return; sorted = true; ranges::sort(B); return; } void insert(ui64 x) { for (auto b: B) x = min(x, b ^ x); if (x == 0) { zero = true; return; } for (auto &b: B) b = min(b, b ^ x); B.push_back(x), sorted = false; return; } int query(int kth = 1) { // query k-th smallest element sort(); if (zero) kth--; ui64 ans = 0; for (auto b: B) { if (kth & 1) ans ^= b; kth >>= 1; } if (kth == 0) return ans; return -1; } ui64 max() { ui64 ans = 0; for (auto b: B) ans ^= b; return ans; } bool check(ui64 x) { for (auto b: B) x = min(x, b ^ x); return x == 0; } void merge(const Basis &oth) { for (auto x: oth.B) insert(x); return; } }; using vi = vector<i64>; i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; vi a(n); for (auto &i: a) cin >> i; ranges::sort(a, greater<>()); Basis b; i64 res = 0; for (auto &i: a) { if (b.check(i)) res += i; else b.insert(i); } cout << res; return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18414642.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示