异或的应用:
1、交换两个数a、b
2、判断a、b是否相等
3、判断一个数二进制中1的个数是奇数还是偶数
4、找到数组a元素之间进行异或运算所能得到的最大值
5、判断一个数x能否被数组a元素之间进行异或运算得到
6、 给出一个数组,询问第k小异或和
异或的数学符号为“⊕”,计算机符号为“xor”
运算规则:
二进制位上数字相同为0,不同为1
0与1 得1 0与0得0
1与0 得1 1与1得0
性质:
1、a^a=0 a^0=a
2、a^b^b=a^0=a
3、若a^b=c,则b=a^c、a=b^c
简单应用:
1、交换两个数a、b
a=a^b;
b=a^b;
a=a^b;
2、判断a、b是否相等
方法:a^b==0?1:0
3、判断一个数二进制中1的个数是奇数还是偶数
方法:将数的二进制逐位进行异或
线性基:
对于含有n个数的数组a,构造出数组a的线性基p1、p2……pk
说明:
取向量组中的两个向量a,b,把a,b中的某一个替换成a xor b, 替换前后向量组中的向量的线性组合得到的空间相同。 通俗的说就是 替换前后能异或出来的值一样,新的向量组能组合出来的数 旧的向量组也能组合出来,因此替换前后能组合出来的数一样。
所以如果把向量组里的向量互相异或, 极大线性无关向量组的大小是不变的。
构造方法:
对于数组a的每一个元素x,从其二进制的最高位到最低位遍历一遍,如果x的二进制第i位为1时,判断此时p[i]是否已经确定,若没有确定,则p[i]=x,结束遍历;若p[i]已经确定,则将x与p[i]进行异或运算
代码实现:
void construction()
{
for(int i=1;i<=n;i++)
for(int j=39;j>=1;j--)//第j位
{
if(!(a[i]>>(j-1)))//右移j-1位
continue;
if(!p[j])
{
p[j]=a[i];
break;
}
a[i]^=p[j];
}
}
线性基的应用:
1、找到数组a元素之间进行异或运算所能得到的最大值
定义最大值为ans,初始化为0,将线性基p从高位到低位与ans进行异或运算,若使ans变大,就进行异或运算,最后ans即是结果
2、判断一个数x能否被数组a元素之间进行异或运算得到
从高到低找到数x的二进制为1的位置i,如果此时p[i]已经确定就将x与p[i]进行异或(注意异或后这个数为1的位置和原数就不一样了),如果p[i]还未确定则说明不能得到。完全遍历后x若为0则说明可以得到,否则不能
代码实现:
int query(int x)
{
for(int i=39;i>=1;i--)
if((x>>(i-1))&1)
{
if(p[i]==0)
return 0;
x^=p[i];
}
if(x==0)
return 1;
return 0;
}
3、 给出一个数组,询问第k小异或和
解法:
首先根据性质一得到的基最高位1的位置互不相同。 记ai为线性基中最高位的1在第i位的 向量, 可以按照i从大到小的顺序,用ai去异或aj (j>i)
这样最后得到的向量组又多了一个优美的性质: 只有ai的第i位是1,其他的第i位都是0.有了这个性质,就容易证明要求的第k小的异1或和,只要把k二进制拆分,第j位是1就异或第j个线性基中的向量。 正确性是显然的...但是表达能力有限,我写不出来。。