线性基小结

来源

https://blog.sengxian.com/algorithms/linear-basis

强烈推荐这篇文章,以下内容大部分来自于此。

前言

这篇是根据以上的博客再结合自己的浅薄理解写的,讲得并不详细,因为弱鸡本人还没完全懂,先学会使用吧。

概述

首先所谓的线性基,就是把若干个整数,看成 n 维 01 向量(通常 n 不会超过 64,即 long long 的范围),然后求这些向量的一个极大无关组。需要注意的一点是,对于这里的 01 向量,只有异或运算。

下面是利用高斯消元求解线性基的代码

void cal() {//a[i]为原来的数,b[i]为新的向量
    for (int i = 0; i < n; ++i)
        for (int j = MAX_BASE; j >= 0; --j)
            if (a[i] >> j & 1) {
                if (b[j]) a[i] ^= b[j];
                else {
                    b[j] = a[i];
                    for (int k = j - 1; k >= 0; --k) if (b[k] && (b[j] >> k & 1)) b[j] ^= b[k];
                    for (int k = j + 1; k <= MAX_BASE; ++k) if (b[k] >> j & 1) b[k] ^= b[j];
                    break;
                }
}

对于最后得到的矩阵,如果第 的主对角线上为 1,此时我们称第 i 位存在于线性基中。

例题

SGU - 275

给定 n(1n100000) 个数a1​​,a2​​,,an​​,请问这些数能够组成的最大异或和是什么?

分析

我们求出向量空间 V 的一组线性基。则答案就是将线性基中所有向量异或起来得到的向量所对应的数

详细证明看最上面的链接。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX_BASE = 63;
ll b[200];
ll a[11234];
void cal(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = MAX_BASE; j >= 0; j--) {
            if (a[i] >> j & 1) {
                if (b[j]) a[i] ^= b[j];
                else {
                    b[j] = a[i];
                    for (int k = j - 1; k >= 0; k--) if (b[k] && b[j] >> k & 1) b[j] ^= b[k];
                    for (int k = j + 1; k <= MAX_BASE; k++) if (b[k] >> j & 1) b[k] ^= b[j];
                    break;
                }
            }
        }
    }
}
int main() {
    int n;
    while (~scanf("%d", &n)) {
        memset(b, 0, sizeof(b));
        for (int i = 0; i < n; i++) {
            scanf("%lld", a + i);
        }
        cal(n);
        ll ans = 0;
        for (int i = 0; i <= MAX_BASE; i++) {
            if (b[i] >> i & 1) ans ^= b[i];
        }
        printf("%lld
", ans);
    }
}

HDU - 3949

给定 n(n10000) 个数 a1​​,a2​​,,an​​,以及 Q(Q10000) 个询问,每次询问这些数(至少一个,不能不选)能够组成的异或和中第 k 小的数是什么(去掉重复的异或和)。

分析

我们求出向量空间 V 的一组线性基 B。因为向量空间中的任意向量u 均可以被表示称B 中向量的唯一线性组合,所以B 中的任意非空子集都可以构成一个向量且互不重复。所以这些数能够组成的异或和的个数为 2​^B​​1,特别的,如果 B<n,则必然存在一个向量组满足线性相关性,这个向量组也就能通过线性组合,异或得到 0,则异或和的个数为 2^B​​。

假设线性基中有 m 个基向量,从小到大依次为 (v0​​,,vm1​​),则第 k=(bx​​b0​​)2​​ 小的数就是: ​(0ix​​)bi​​vi​​

//  Created by Sengxian on 2016/12/5.
//  Copyright (c) 2016年 Sengxian. All rights reserved.
//  HDOJ 3949 线性基
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
inline ll readLL() {
    static ll n;
    static int ch;
    n = 0, ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
    return n;
}

const int MAX_N = 100000 + 3, MAX_BASE = 60;
int n, zero = false;
ll a[MAX_N], b[MAX_BASE + 3];
vector<ll> mmap;

void prepare() {
    int cnt = 0;
    memset(b, 0, sizeof b);
    for (int i = 0; i < n; ++i)
        for (int j = MAX_BASE; j >= 0; --j)
            if (a[i] >> j & 1) {
                if (b[j]) a[i] ^= b[j];
                else {
                    b[j] = a[i], cnt++;
                    for (int k = j - 1; k >= 0; --k) if (b[k] && ((b[j] >> k) & 1)) b[j] ^= b[k];
                    for (int k = j + 1; k <= MAX_BASE; ++k) if ((b[k] >> j) & 1) b[k] ^= b[j];
                    break;
                }
            }
    zero = cnt != n;

    mmap.clear();
    for (int i = 0; i <= MAX_BASE; ++i)
        if (b[i]) mmap.push_back(b[i]);
}

ll query(ll k) {
    if (zero) k--;
    if (k >= (1LL << (int)mmap.size())) return -1;
    ll ans = 0;
    for (int i = 0; i < (int)mmap.size(); ++i) if ((k >> i) & 1)
        ans ^= mmap[i];
    return ans;
}

int main() {
#ifdef DEBUG
    freopen("test.in", "r", stdin);
#endif
    int caseNum = readLL();
    for (int t = 1; t <= caseNum; ++t) {
        n = readLL();
        for (int i = 0; i < n; ++i) a[i] = readLL();

        prepare();

        printf("Case #%d:
", t);
        int q = readLL();
        for (int i = 0; i < q; ++i) printf("%lld
", query(readLL()));
    }
    return 0;
}

小结

线性基的题型相对比较固定,看到下面的类型基本上都是线性基了:

  1. 最大异或和
  2. 第 kk 大异或和/异或和是第几大
  3. 求所有异或值的和
原文地址:https://www.cnblogs.com/fht-litost/p/9562807.html