异或运算

异或的应用:

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个线性基中的向量。  正确性是显然的...但是表达能力有限,我写不出来。。

你可能感兴趣的:(位运算)