线性基:处理异或的有力工具。线性基是一个集合,由已知的集合求出。线性基有如下性质。
先看线性基是如何构造的:
其中 d[] 数组保存线性基各元素,d[i] 的第 i+1位是1,且第i+1位是最高位。d[i] 是由若干个已知集合中的元素异或得到的。
LL d[60]; //保存线性基各元素
void Add(LL x){
for(int i=60; i>=0; i--){
if(x >> i) { //第i+1位是1
if(d[i]) x ^= d[i];
else { d[i] = x; break; }
}
}
}
- 集合中的任意元素可以由线性基中的某些元素异或得到。
- 线性基中的任意若干元素异或起来都得不到0。
- 线性基的元素个数一定,且在保证性质1的条件下,元素个数是最少的。
各性质相关证明 https://blog.youkuaiyun.com/a_forever_dream/article/details/83654397
有了线性基之后可以求:在一个数列中取若干数进行异或能得到的最大值、最小值、第 k 小值。
最大值:贪心即可,从高位到低位
LL Xormax(){
LL ans = 0;
for(int i=60; i>=0; i--)
if((ans^d[i]) > ans)
ans ^= d[i];
return ans;
}
最小值:是线性基中最小的数
LL Xormin(){
for(int i=0; i<=60; i++)
if(d[i]!=0) return d[i];
}
第k小值:要将 d[] 中的数组处理一下。将每个d[i]二进制中除最高位之外的,且能被d[j]最高位的1异或掉的1消去。换句话说就是保证只有d[i] 的二进制的第 i+1位是1,其他d[j] 的第 i+1 位一定是0。
例如:1001101 处理完之后: 1000100
1011 1001
10 10
LL k_thmin(int k){
if(k == 1 && tot < n) return 0;//假如k=1(求最小),并且原来的序列可以异或出0,就要返回0,tot表示线性基中的元素个数,n表示序列长度
if(tot < n) k--; //类似上面,去掉0的情况,因为线性基中只能异或出不为0的解
for(int i=1; i<=60; i++)
for(int j=1; j<=i; j++)
if(d[i] & (1LL<<(j-1)))
d[i] ^= d[j-1];
LL ans = 0;
for(int i=0; i<=60; i++)
if(d[i] != 0) {
if(k&1) ans ^= d[i];
k >>= 1;
}
return ans;
}
题目:
http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1002&cid=848
一个数列,若干操作,两种类型操作:0、询问 [l, r] 内取若干元素异或和最大值。1、在数列末尾添加一个数x。其中的 l ,r , x 的值还有些其他处理。
思路:先只考虑第一个条件,询问区间内的异或和最大值,可以用一个pos数组记录每次Add操作时的位置,求最大值时加上一个判断条件即可。考虑第二个条件,每次加上一个元素时相当于再进行一次Add操作,每次记录添加一个值时的线性基,把数组开成二维即可。
与正常线性积区别就是:正常情况下线性积中保存的是先插进来的数,而现在要尽量将后插入的数存在线性积中,所以有插入过程中多了一步判断交换步骤。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
struct Line_Base{
const static int sz = 35;
int n, a[maxn][sz], b[maxn][sz];
void build(){n=0;}
void add(int x){
int cur = ++n;
for(int i=31; i>=0; --i){
a[n][i] = a[n-1][i];
b[n][i] = b[n-1][i];
}
for(int i=31; i>=0; --i){
if(x >> i){
if(!a[n][i]){
a[n][i] = x;
b[n][i] = cur;
break;
}else {
if(cur > b[n][i]) swap(a[n][i], x), swap(b[n][i], cur);
x ^= a[n][i];
}
}
}
}
int query(int l, int r){
l = l%n + 1; r = r%n + 1;
if(l > r) swap(l, r);
int ret = 0;
for(int i=31; i>=0; --i)
if(b[r][i] >= l)
ret = max(ret, ret^a[r][i]);
return ret;
}
}b;
int main()
{
int t, n, m, x, y, op;
scanf("%d", &t);
while(t--){
b.build();
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++) scanf("%d", &x), b.add(x);
int ans = 0;
for(int i=0; i<m; i++){
scanf("%d", &op);
if(op == 0){
scanf("%d%d", &x, &y);
printf("%d\n", ans=b.query(x^ans, y^ans));
}else scanf("%d", &x), b.add(x^ans);
}
}
}