【算法笔记】线性基
1|0线性基
定义:给定数集s,以异或运算张成的数集与S相同的极大线性无关集,称为原数集的一个线性基。
性质 :
- 原数集的任意一个数都能有线性基内部的一些数异或得到。
- 线性基内部任意数异或不为 0
- 线性基内数唯一,且保证性质一的情况下,数的个数最少。
- 线性基内每个数的最高有效位各不相同。
如何判断原数集能否异或出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的情况,这个可以用线性基来维护。
值最大可以贪心,优先插入魔力值大的。假设集合内存在a⊕b⊕c⊕d=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
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
本文链接:https://www.cnblogs.com/PHarr/p/18414642.html
关于博主:前OIer,SMUer
版权声明:CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律