线性基(25.10.5)
线性基
概述
使用特殊方法得到A的子集B,使B可以通过不同元素乘除加减得到A的每个元素,B是最简状态,称为A的线性基
可以去解决线性相关/无关的问题,(两个向量通过不同的加减变化会不会变成另一个数)
思考方向
- 发现题目中有存在使用不同元素乘除加减组合,考虑线性基
- 发现(最大)异或和的问题,尝试考虑线性基
普通异或线性基
贪心线性基
- 主要生成的B是所有元素异或和的可能不会有0
- 存储的是原数本身,所以需要再贪心一下才能再输出
- 本质是尝试一个数和其它的所有数异或和,看看能不能有新的位数出现
#include <bits/stdc++.h> using namespace std; #define ll long long ll p[64]; int n; void ins(ll x) { for (int i=63; i>=0; i--) { if (!(x>>i)) continue; if (!p[i]) { p[i] = x; break; } x^=p[i]; } } int main() { cin>>n; ll a; for (int i=1; i<=n; i++) { cin>>a; ins(a); } ll ans=0; for (int i=63; i>=0; i--) { ans = max(ans,ans^p[i]); } cout<<ans<<endl; return 0; }
高斯消元线性基
把数已经消成了通解(如x1+x2+x3+x4化简后只存在单个主元),这样就可以直接操作
这里的只是把所有数的主元以梯形形式呈现,没有进一步的归一化操作,但是已经够用了
异或高斯消元线性基
#include <iostream> using ull = unsigned long long; // 定义无符号长整型,避免符号位干扰 constexpr int MAXN = 1e5 + 5; // 最大数据量,1e5+5 // 辅助函数:判断num的第deg位是否为1(返回该位的值,0或2^deg) ull deg(ull num, int deg) { return num & (1ull << deg); // 1ull确保移位操作按64位无符号数处理 } ull a[MAXN]; // 存储原始数据的数组,后续会被改造成线性基 using std::cin; using std::cout; int main() { cin.tie(nullptr)->sync_with_stdio(false); // 关闭同步,加速输入输出 int n; cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; // 读入n个数据 int row = 1; // 记录当前构建的线性基行数(有效元素数) // 从最高位(63位)向最低位(0位)处理,构建线性基 // ~col 等价于 col >= 0(当col=-1时,~col=0,循环结束) for (int col = 63; ~col && row <= n; --col) { // 寻找当前列(第col位)中,从row行开始第一个该位为1的数 for (int i = row; i <= n; ++i) { if (deg(a[i], col)) { // 找到第col位为1的数 std::swap(a[row], a[i]); // 将该数交换到当前行(row行) break; } } // 如果当前行的第col位为0,说明该位无法找到线性基元素,跳过该位 if (!deg(a[row], col)) continue; // 消去其他行中第col位的1,确保只有row行在第col位为1(高斯消元核心) for (int i = 1; i <= n; ++i) { if (i == row) continue; // 跳过当前行(保留线性基元素) if (deg(a[i], col)) { // 若其他行的第col位为1 a[i] ^= a[row]; // 用当前行消去该位的1 } } ++row; // 成功构建一行线性基,处理下一行 } // 计算最大异或和:线性基中所有非零元素异或的结果 ull ans = 0; for (int i = 1; i < row; ++i) { // row-1是线性基的有效元素个数 ans ^= a[i]; } cout << ans << '\n'; return 0; }
实数高斯消元线性基
#include <iostream> using ull = unsigned long long; // 定义无符号长整型,避免符号位干扰 constexpr int MAXN = 1e5 + 5; // 最大数据量,1e5+5 // 辅助函数:判断num的第deg位是否为1(返回该位的值,0或2^deg) ull deg(ull num, int deg) { return num & (1ull << deg); // 1ull确保移位操作按64位无符号数处理 } ull a[MAXN]; // 存储原始数据的数组,后续会被改造成线性基 using std::cin; using std::cout; int main() { cin.tie(nullptr)->sync_with_stdio(false); // 关闭同步,加速输入输出 int n; cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; // 读入n个数据 int row = 1; // 记录当前构建的线性基行数(有效元素数) // 从最高位(63位)向最低位(0位)处理,构建线性基 // ~col 等价于 col >= 0(当col=-1时,~col=0,循环结束) for (int col = 63; ~col && row <= n; --col) { // 寻找当前列(第col位)中,从row行开始第一个该位为1的数 for (int i = row; i <= n; ++i) { if (deg(a[i], col)) { // 找到第col位为1的数 std::swap(a[row], a[i]); // 将该数交换到当前行(row行) break; } } // 如果当前行的第col位为0,说明该位无法找到线性基元素,跳过该位 if (!deg(a[row], col)) continue; // 消去其他行中第col位的1,确保只有row行在第col位为1(高斯消元核心) for (int i = 1; i <= n; ++i) { if (i == row) continue; // 跳过当前行(保留线性基元素) if (deg(a[i], col)) { // 若其他行的第col位为1 a[i] ^= a[row]; // 用当前行消去该位的1 } } ++row; // 成功构建一行线性基,处理下一行 } // 计算最大异或和:线性基中所有非零元素异或的结果 ull ans = 0; for (int i = 1; i < row; ++i) { // row-1是线性基的有效元素个数 ans ^= a[i]; } cout << ans << '\n'; return 0; }
题目
一道普通模板
[P3857 TJOI2008] 彩灯 - 洛谷
发现存在两种状态相互异或和,所以考虑线性基
[P4570 BJWC2011] 元素 - 洛谷
发现题目需要去找序号之间的 异或线性不相关,考虑线性基,直接去存,可以保证线性不想关
[P3265 JLOI2015] 装备购买 - 洛谷
题目直接表明需要去找向量之间的线性不想关,所以延伸出实数向量的线性基(只能通过高斯消元写)(通过贪心的话难以向下实现)
以上均为线性基的模板题目
tip(25.10.5):目前线性基还有漏洞,没有进一步去完善,在NOIP之前准备补齐