NOIP中的数学---第1课 位运算

本文介绍了如何利用位运算技巧,通过异或(XOR)操作快速找出数组中出现奇数次的数字。通过实例演示了如何通过与(&)、或(|)、异或(^)等操作,简化查找过程并降低时间复杂度。适合对位运算感兴趣或解决实际编程问题的开发者。
摘要由CSDN通过智能技术生成

位运算常用运算

位操作是一种速度非常快的基本运算:有左移、右移、与、或、非、异或等运算。

左移:左移一位,相当于某数乘以2,比如110左移1位变为1100 ,右边的空位补0,6变为12,表示为(110<<1)
因此左移x位,相当于该数乘以2x

在右移时,需要注意符号位问题。对无符号数,右移时左边高位移入0。对于有符号的值,如果原来符号位为0(该数为正),则左边也是移入0,如果上例表示的那样,如果符号位原来为 1(该数为 负),则左边移入的 0还是1,要取决于所用的计算机系统。移入0 称为 逻辑右移,即简单右移。移入1称为算术右移。

右移:右移一位,相当于某数除以2,比如110右移1位变为011,6变为3,表示为(110>>1)
因此右移x位,相当于该数除以2x

与运算:按位进行“与”运算,两数同一位都为1时结果为1,否则为0。例如:101&110=100。

或运算:按位进行“或”运算,两数同一位都为0时结果为0,否则为1。例如:101|110=11l。

非运算:按位取反。例如~101=010。
“异或”运算符(^) 它的规则是:若参加运算的两个二进制位值相同则为0,否则为1.
常见用法:反转二进制,只需要将想要反转的位异或1就可以, 如:1010^1111=0101

位运算实用”杀招“

若当前二进制位为S,对S有下列操作。

判断第i位是否为0:(S&(1<<i)==0,意思是将1左移i位与S进行与运算后,看结果是否为0

将第i位设置为1:S|(1<<i),意思是将1左移i位与S进行或运算。

将第i位设置为0:S&~(1<<i),意思是S与第i位为0,其余位为1的数进行与运算。

例如:S=1010101,i=5。注意二进制位从0开始计数,所以i=5实际上是第6位
S&(1<<i):1010101&0100000=000000
在这里插入图片描述
课堂练习
一个奇数次

输入一个长度为n的数组,考虑所有不同的数字,有且只有一个数字出现了奇数次。 比如对于1 2 3 1 2 3 1,我们考虑所有不同的数字1 2 3,有且只有1出现了奇数次(3次) 输出这个出现了奇数次的数字。 1 <= n <= 100000 1 <= a[i] <= 10^9
输入格式
第一行一个整数n, 接下来一行n个整数,表示输入的数字。
输出格式 一行一个数字,表示出现了奇数次的数字。
输入样例

7
1
2
3
1
2
3
1
输出样例

1

[解题思路]
一个比较简单的思路,对数组排序,然后逐个统计每个数字出现的次数,复杂度O(nlogn) 。
位运算异或^: 根据 XOR运算的性质,a ^ a=0 。如果某个数出现了两次,那么他们 异或在一起的值恰好为0。
进一步扩展,出现偶数次的数异或和也一定为0,出现奇数次的数异或和一定是这个数本身。因此只要将所有数异或 在一起,剩下的那个数就是出现奇数次的数。

#include<bits/stdc++.h> using namespace std;

int n;

int main() {
int x;
while (~scanf("%d", &n)) { int ans = 0;
for (int i = 0; i < n; i++) { scanf("%d", &x);
ans ^= x;
}
printf("%d\n", ans);
}
return 0;
}

更多练习

1、二进制距离之和
2、区间XOR
3、最大异或和
4、两个奇数次

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信奥教练Andy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值