一. 异或的性质
1. A xor A = 0
2. A xor 0 = A
3. 若 A xor B = C,则 A xor C = B,B xor C = A
二. 应用
1. 根据性质1,2,可以实现a,b交换
void swap()
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
2. 给定2n+1 个数,只有一个数只出现一次,将所有数异或,结果便是落单的数。
三. 线性基
1)什么是线性基?
基就是基底的意思,通过线性基中元素 xor 出的数的值域与原来的数组 xor 出数的值域相同,类似于空间 x轴、y轴、z轴的单位向量,对单位向量乘上不同的系数,可以得到空间的所有向量,原数组相当于空间所有的向量,单位向量相当于线性基。
2)构造线性基:
对每一个数 x 从高位到低位扫,扫到第 i 位为 1 时,若线性基中第 i 位不存在,则 a[i] = x 并结束此数的扫描,否则令x = x ^ a[i],继续往下扫。所以一个数要么加入线性基,要么变为0。
举个例子:先加入,最高位为1是第4位,则a[4] = ,再加入,这个数也是最高位为1也是第4位,而 a[4] 已经存在,所以,同理会有a[2] = ,根据性质1,a[2] xor a[4] 就可以得到消失的
void Create_linear_basis(ll x)
{
for(int i=62;i>=0;i--)
{
if(x&(1ll<<i))
{
if(!ans[i])
{
ans[i] = x;
return ;
}
x = x ^ ans[i];
}
}
}
3)求原数组中异或的最大值(等价于求线性基中的异或最大值)
从高往低扫 a[x],若异或上 a[x] 使答案变大,则异或
题目:https://www.luogu.org/problemnew/show/P3812#sub
ll MAX_xor()
{
ll ans = 0;
for(int i=62;i>=0;i--)
{
if(ans^a[i] > ans)
ans = ans^a[i];
}
return ans;
}
4)求原数组异或的最小值(相当于求线性基的最小值)
根据线性基的构造法则,线性基中存在值的最低位的值就是最小值
ll MIN_xor()
{
for(int i=0;i<=62;i++)
{
if(ans[i])
return ans[i];
}
}
5)原数组通过任意次异或,能否得到x?
和构造线性基一样,如果最后 x 变为0,那就可以,当然需要特判一下x = 0,如果构造时有数最后为0,也就是 n!=m ,则就行
题目:https://www.nowcoder.com/acm/contest/180/D
bool Whether_xor(ll x)
{
if(x==0) return n==m? false:true;//m为加入线性基元素的个数,n为原数组总的个数
for(int i=62;i>=0;i--)
{
if(x&(1ll<<i))
{
if(!a[i])
return false;
else
x = x ^ ans[i];
}
}
return x==0? true:false;
}
6)求原数组第 k 小的异或值
需要改造一下线性基,目的是让第x位为1的元素 (即a[x]) 在线性基中只有他的第x位为1(唯一性),比如 a[4] = ,那就让其他元素的第四位都为0,处理完后再把a数组的元素从小到大依次放进b数组,求第k小时,只要把k二进制拆分,第j位是1就异或 b[j-1],比如求第小,答案就是 b[0] ^ b[1],b数组相当于二进制位,,只不过这里是b[0] ^ b[1],再举个例子:b[0] = ,b[1] = ,b[2] = ,那么最小是b[0],其次是b[1],再其次是b[0]^b[1],接下来是b[2],b[2]^b[0],b[2]^b[1],最后是b[0]^b[1]^b[2],原理就和改造线性基有关,读者可以根据上文提到的唯一性结合二进制意会便可明白。(ps:同样要考虑n和m的关系)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3949
#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll a[64];
ll b[64];
void linear_basis_a(ll x) //构造线性基
{
for(int i=62;i>=0;i--)
{
if(x&(1ll<<i))
{
if(!a[i])
{
a[i] = x;
return ;
}
x = x ^ a[i];
}
}
}
int linear_basis_b() //改造线性基
{
int m = 0;
for(int i=0;i<=62;i++)
{
for(int j=i-1;j>=0;j--)
{
if(a[i]&(1ll<<j))
a[i] = a[i] ^ a[j];
}
if(a[i])
b[m++] = a[i];
}
return m; //上文中m
}
ll kth_xor(ll k,int m) //求第k小
{
ll ans = 0;
for(int i=0;i<m;i++)
{
if(k&(1ll<<i))
ans = ans ^ b[i];
}
return ans;
}
int main()
{
int t,n,q,p;
ll x,k;
cin>>t;
for(int i=1;i<=t;i++)
{
memset(a,0,sizeof(a));
cin>>n;
p = n;
while(p--)
{
cin>>x;
linear_basis_a(x);
}
printf("Case #%d:\n",i);
int m = linear_basis_b();
ull max_num = ((ull)1)<<m; //线性基性质(1)
cin>>q;
while(q--)
{
cin>>k;
if(n!=m) k--; //如果n!=m,则有一个最小值0
if((ull)k>=max_num) cout<<-1<<endl;
else cout<<kth_xor(k,m)<<endl;
}
}
return 0;
7)线性基性质
(1)线如果线性基有m个数,则最多可异或出 个不同值
(2)线性基中元素任意异或,都不会等于0
(3)要注意n和m的关系