博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线性基小结
阅读量:6402 次
发布时间:2019-06-23

本文共 3930 字,大约阅读时间需要 13 分钟。

来源

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

前言

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

概述

首先所谓的线性基,就是把若干个整数,看成 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 
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\n", 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 
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
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:\n", t); int q = readLL(); for (int i = 0; i < q; ++i) printf("%lld\n", query(readLL())); } return 0;}

小结

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

  1. 最大异或和
  2. 第 kk 大异或和/异或和是第几大
  3. 求所有异或值的和

转载于:https://www.cnblogs.com/fht-litost/p/9562807.html

你可能感兴趣的文章
自定义元素探秘及构建可复用组件最佳实践
查看>>
区块链是一个公共数据库,要放在一个块内
查看>>
Jenkins 用户文档(目录)
查看>>
系统常见指标
查看>>
使用crond构建linux定时任务及日志查看
查看>>
地图绘制初探——基于maptalks的2.5D地图绘制
查看>>
SpringBoot2.0之七 实现页面和后台代码的热部署
查看>>
Git 仓库大扫除
查看>>
设计模式-单例模式
查看>>
es6基础0x014:WeakMap
查看>>
九种 “姿势” 让你彻底解决跨域问题
查看>>
php中mysqli 处理查询结果集总结
查看>>
你不知道的JavaScript运算符
查看>>
小程序开发注意事项
查看>>
ECMAScript7规范中的instanceof操作符
查看>>
Hadoop HDFS原理分析
查看>>
【webpack4】基本配置和入门api
查看>>
Mac使用ssh公钥登录Linux
查看>>
【366天】跃迁之路——程序员高效学习方法论探索系列(实验阶段124-2018.02.06)...
查看>>
POJ3070-Fibonacci(矩阵快速幂)
查看>>