线性基

定义

线性基用于解决类似于: "给定 \(n\) 个数, 取任意个数, 使得异或值最大"此类问题

本质上还是求出多个数最大和最小的异或和,由于给出的数目较大, 如果直接一一比较的话时间复杂度较大,可以通过线性基优化, 假如有 \(n\) 个, 其中最大的数二进制位数为 \(m\) 位, 那么线性基可以把原来的 \(2^n\) 的组合优化到 \(2^m\), 一次的时间复杂度为 \(m\). 虽然理论上有 \(2^n\) 种组合, 但是由于: 任意两个数 \(a,b\) 做异或运算之后不会比他们两者的最高二进制位还要高, 所以 \(2^n\) 种组合中最大的数也就是\(2^m - 1\) 也就是异或结果会在: \([0\) ~ \(2^m - 1]\) 这个区间内

这里介绍一下什么是线性基, 举个形象的例子来说, \(A = [2, 3, 5, 6, 7]\) 那么它的任意组合有 \(2^n - 1\) 种, 对 \(A\) 中所有的数做异或运算之后得到所有的可能性: \([0, 1, 2, 3, 4, 5, 6, 7] = 8\) 种, \(A\) 中最大的数字二进制位是 \(3\) 位, 此时我们有个线性基 \(P = [5, 2 , 1]\), 有 \(2^3 - 1 = 7\) 种, 注意线性基不能处理异或为 \(0\) 的可能性, 故此时为 \(7\), 注意, 线性基不是唯一的

算法

接下来考虑如何构造线性基, 下面通过分析两个例子
\((1).\) 首先分析一种特例, \(A\) 中所有的元素的二进制位均不同, 那么 \(A\) 自身就是一个线性基, 例如 \(A = [1, 3, 9] = [1, 11, 1001]\), 那么很明显是 \(1, 2, 4\) 位, 均不同, 此时 \(A\) 自己就是自己的线性基, 因为如果我需要异或出一个长度为 \(4\) 的数, 那么 \(P\) 中必须有长度为 \(4\) 的数, 对于其他数字同理
\((2).\) 对于 \(A\) 中的数字较为繁杂, 含有相同位数的数字, 我们这样处理, 对于任意相同的数字, 直接异或即可. 先讨论两个位数相同的线性基构造. 设 \(A = [a_1, a_2]\) 且两者二进制位数相同, 那么其线性基为: \(P = [a_1, a_1\oplus{a_2}]\), 下面给出证明: 对于 \(A = [a_1, a_2]\) 的组合: \([a_1, a_2, a_1 \oplus{a_2}]\) , 而 \(P\) 的组合为: \([a_1, a_1 \oplus {a_2}, a_1 \oplus{a_1} \oplus{a_2} = 0 \oplus{a_2} = a_2]\), 由此可见, 两者相同, 那么我们对于相同位的直接做异或处理即可.

  1. 最小异或和
    由于最小异或和就一个值, 那么不是 \(0\) 就是位数最小的那个数字
  2. 最大异或和
    考虑贪心, 对于每一位的数字, 我们直接做异或看看是否增大, 若增大, 我们就直接加进去, 为什么这样是对的? 因为我们从大到小开始, 对于最大的, 肯定不会存在一个其它的数可以到达最高位. 对于次大的, 若异或后对答案有益, 我们就异或, 因为不会存在一个更大的数可以使答案更优, 换句话说, 比它小的元素任意的组合都不会比它大.

例题

下面给出例题: 线性基

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
signed main()
{
    std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n; cin >> n;
    vector<int> p(65);
    bool zero;
    auto insert = [&](int x) -> void{
        for(int i = 61; i >= 1; i--){
            if(x >> (i - 1)){
                if(p[i] == 0){
                    p[i] = x;
                    return;
                }
                else x ^= p[i];
            }   
        }
        zero = true;
    };
    for(int i = 1; i <= n; i++){
        int x; cin >> x;
        insert(x);
    }
    int res = 0;
    for(int i = 61; i >= 1; i--){
        res = max(res, res ^ p[i]);
    }
    cout << res << '\n';
    return 0;
}
posted @ 2024-07-30 03:04  o-Sakurajimamai-o  阅读(30)  评论(0)    收藏  举报
-- --