异或 问题

问题定义:有2n+1个数,只有一个单着,别的都是成对的,找出这个单着的数。比如:2 1 3 2 1。3是答案。

思路一:暴力搜索——每个数都和其他数比较,找不到相同的,就得到了结果。时间复杂度为o(n2)

思路二:排序搜索——先给序列排个序,之后从前往后一对一对的找,直到不是成对的为止。时间复杂度,怎么也得o(nlgn)

思路三:异或计算,一趟搞定。时间复杂度o(n)

直接看思路三:
原理:
异或操作(^)——(对于操作)相同为0,相异为1.比如:1^0 = 1, 1 ^1=0

这样:

  • 两个相同的数异或就为0
  • 任何数和0异或为自己(转化到位。1^0 = 1, 0 ^0=0

例如:5 ^ 5 = 0

      5 ^ 0 = 5

 

对于本题:2 1 3 2 1,都异或一下:相同的(2^2,1^1) 为0,剩下的3和0异或为自身3。(注:异或具有交换律)

代码:

#include <stdio.h>
int main()
{
      int a[5] = {2, 1, 3, 2, 1};
      int aim = a[0];
      for(i = 1; i < 5; i++)
       {
            aim = aim ^ a[i];
        }
        printf("Result:", aim);
        return 0;
}

异或在这方面挺好,再来个应用:

不用第三个数直接交换两个数:

#include <stdio.h>
void swap(int *a, int *b)
{
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
int main()
{
    int a=3, b=5;
    swap(&a, &b);
    printf("%d\n%d", a, b);
    return 0;
}

转自: jihite



hihoCoder #1509 : 异或排序

描述

给定一个长度为 n 的非负整数序列 a[1..n]

你需要求有多少个非负整数 S 满足以下两个条件:

(1).0 ≤ S < 260

(2).对于所有 1 ≤ i < n ,有 ( a[ i] xor S) ≤ ( a[ i+1] xor S)


题解:(转自:伪·ACMer

由于每一个数都有一个二进制表示,由数论知识我们知道比较两个数是从高位开始比较的,当比较到某一位发现一个数该位为1而另一个数该位为0时就知道前面那个数一定比后面那个要大,而再考虑异或的性质:

0 Xor 1 = 1

1 XOR 1 = 0

0 XOR 0 = 1

假设我们有两个数x,y,不妨设x>y,那么存在第i位使得x与y在更高的位都相等但是x在该位为1而y在该位为0,由于 1 XOR 1 = 0, 0 XOR 1 = 1,那么假若存在S,只要S在第i位为1,就能使得 (x XOR S) <= (y XOR S)

反正只要该位符合题意就好,其他位是任意的,显然这个任意的数目就是我们要求的了0。0思路大体也出来的

先生成一个S[60]的数组,扫描一遍,检索两个数是小大还是大小关系,小大关系的话在那个位置0,大小关系的话在那个位置成1,注意如果那位本来有数的话要看看是否一致,不一致说明这个东西他不存在

最后看看有多少个位置是没有被固定的,那么2的那个那个数目次方就是所求。


#include <iostream>
#include <stdio.h>
#include <string.h>
#define LL long long

using namespace std;

const int ma=65;
LL num[ma],pow[ma];
int flag[ma];

LL quick_pow(int x) ///计算2^k(2的k次方)
{
    if(pow[x]!=0) return pow[x];
    return pow[x]=quick_pow(x/2)*quick_pow(x-x/2);
}

int fi(LL x)
{
    int i;
    for(i=60-1;i>=0;--i)
        if(x>=quick_pow(i)) break;
    return i;
}

int main()
{
    memset(pow,0,sizeof(pow));
    pow[0]=1;
    pow[1]=2;
    quick_pow(60);

    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&num[i]);

    memset(flag,-1,sizeof(flag));

    LL cnt;
    int fg,k;
    for(int i=1;i<n;++i)
    {
        cnt=num[i]^num[i+1];
        fg=(num[i]>num[i+1]);
        k=fi(cnt);

        if(k==-1) continue;
        if(flag[k]==-1) flag[k]=fg;
        else if(flag[k]!=fg)
        {
            printf("0\n");
            return 0;
        }
    }

    k=0;
    for(int i=0;i<60;++i)
        if(flag[i]==-1) k++;

    printf("%lld\n",quick_pow(k));


    return 0;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值