一、位运算
1.位运算概述
主要学会这些运算在编程中的编写
二进制逻辑左移一位表示乘以2 右移一位除以2
2.位运算的高级操作
3.其他操作
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<math.h>
void swap(int& a, int& b) {
a ^= b;
b ^= a;
a ^= b;
}
int count(int x) {
int cnt = 0;
while (x) {
//如果x是形如aaaa100..的话,x-1就会变成 aaaa0111..
//从左到右的几个0都会变成1,然后二者进行与运算,那么就会消除掉这个1
//消除几次1以后x变成0,就说明有几个1
x = x & (x - 1);
cnt++;
}
return cnt;
}
int main() {
int a = 2;
int b = 9;
int c = 0, d = 0, e = 0;
//1.位运算交换两个整数
swap(a, b);
cout << a << ' ' << b << endl;//输出 9 2
//2.逻辑左移和逻辑右移
a = a << 1;// 4*2 = 8
b = b >> 1;// 2/2 = 1
cout << a << ' ' << b << endl;//输出 18 1
//3.或运算 与运算 异或运算
c = a || b;
d = a & b;
e = a ^ b;
cout << c << ' ' << d << ' ' << e << endl;//输出1 0 19
//4.判断是奇数还是偶数 取最低位与1相与
//取末k位,x&(1<<k-1)
e = e & (1 << 0) & 1;//与1相与
cout << e << endl;//输出1,说明是最后一位是1,则位奇数
//5.改变正负性
a = ~a + 1;//取反是0变1 1变0
cout << a << endl;//加1得到的才是真正取反的数字
a = ~a + 1;
//6.统计二进制位中 1 的个数
int sum = count(a);
cout << sum << endl;//18写成二进制数是10010,两个1
return 0;
}
二、例题——异或运算
思路:
异或运算就是 相同得0,不同得1.
0^x = x
1^x = x 如果进行偶数次运算的话会反转成原来的数字
可以根据异或运算的特性,先筛选出特殊情况
> 记住:操作可以选择加在对方身上
- 先把所有数字看成二进制,高位用0补齐
- 讨论输赢情况
Alice输入a, 输出A,Bob输入b,输出B,
如果sum = x1 ^ x2 ^ x3 ^ …^xn
①A ^ B = a ^ b ^ sum = 0的话,说明sum = 0 , 则Alice和Bob一定平手
②讨论不等于0的情况,不等于0的话就从最高位开始向下计算1的个数,并且存到num[i]中,num[i]表示第i位有多少个1
num[i] = 1时,先手必赢
num[i] = 偶数,接着考虑下一位
num[i] > 1 且为奇数时,0的个数为偶数的话,先手赢,输出1
num[i] > 1 且为奇数时,0的个数为奇数的话,后手赢,输出-1
因为如果选择0相当于轮空,没有变化。
因此要争抢1,奇数个1的话,偶数个0的话,先手赢。
1和0的个数都为奇数的话,二者都要争抢0,让最后一个1到自己手中。假如Alice先抢0,最后一个1在bob手中,它可以加到alice身上,如果Alice先抢1的话,Bob也可以加在alice身上,无论如何都是输
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//根据题目,初始值都为0
//如果只有一个数的话,表明只有一局,只有一局就是谁先手谁胜或者平手(是0),这是特殊情况
long long cnt0, cnt1;
long long sum;
long long a[200020];
//判断胜负
int judge() {
//偶数个1,继续判断
if (cnt1 % 2 == 0)
return 0;
if (cnt1 == 1) {//1个1先手必胜
return 1;
}
//奇数个0,1,后手必赢
if (cnt1 % 2 != 0 && cnt0 % 2 != 0) {
return -1;
}
else
return 1;
}
//统计0,1个数
void solve(int n) {
sum = 0;
for (int i = 1; i <= n; i++) {
//scanf %d 后面不要添加任何东西
//scanf("%lld", &a[i]);//一局询问次数
cin >> a[i];
sum ^= a[i];
}
//平局
if (sum == 0) {
//printf("0\n");
cout << 0 << endl;
return;
}
//高位开始枚举,获取0和1的个数
for (int i = 20; i >= 0; i--) {//最高位
cnt0 = cnt1 = 0;
//这个for循环内计数,一直循环完一组数据的第一位
for (int j = 1; j <= n; j++) {
//逻辑右移
// long long x = a[j];
if ((a[j] >> i) & 1) {//不用while循环是因为这只是一层循环,判断完第一个数的第一位,要去判断第二个数的第一位
//当前层全部判断完成后就判断是不是可以分出胜负,不可以的话再判断下一位
cnt1++;
//x >>= 1;//表示是x等于逻辑右移后的一位
}
else {
cnt0++;
}
}
int res = judge();
if (res == 0) {
continue;
}
else {
//printf("%lld\n", res);
cout << res << endl;
return;
}
}
}
int main() {
long long t;
long long n;
//scanf("%lld", &t);//t局
cin >> t;
while (t--) {
//scanf("%lld", &n);//数列长度
cin >> n;
solve(n);
}
return 0;
}