蓝桥杯备赛Day10 位运算

位运算

1.要点

与:&
或:|
异或:^
非:~
异或运算性质:
(1)x^x=0
(2)x^0=x
(3)a^b^b=a(1,2推出)
(4)a^b=c->a=b^c(两侧同异或b)
位运算按补码计算

  • 正数的补码就是正数本身;
  • 负数的补码 = 负数的绝对值正数补码取反 + 1
    正数右移要用unsigned int最后才会变0(int高位补1)
    (1)将一个数乘(除) 2 的非负整数次幂
    x<<i(乘以2的i次方) x>>i(除以2的i次方)
    (2)判断数字奇偶性:
    x&1(x为奇数,二进制最后一位为1,x&1=1,反之x为偶数,二进制最后一位为0,x&1=0.优化x%2)
    (3)判断数字某一位1还是0:
    (x>>i) & 1
    (3)修改二进制中的某一位
    1.改为1
    x | (1<<i)(将x从右数第i位改为1)
    x:10010
    1<<3:01000
    结果:11010
    2.改为0(将x从右数第i位改为0)
    x & ~(1<<i):11010
    1<<3:01000
    ~(1<<3):10111
    结果:10010
    (4)快速判断一个数字是否为2的幂次方
    x & (x-1)(若为0,x是2的幂次方,反之不是)
    x:00100
    x-1:00011
    x & (x-1):00000
    x为2的幂次方,x-1肯定借位,最终0和1错位,结果为0
    x:00110
    x-1:00101
    x & (x-1):00100!=0
    x不是2的幂次方,x-1不会借最高的1位,最终里面肯定有个1,结果不为0
    (5)获取二进制位中最低位的1
    lowbit(x)=x & -x
    x:010010(18)
    -x:101101+1=101110(负数的绝对值取反 + 1)
    lowbit(x):000010

2.题目

2023异或和之和

重点:
(1)构造异或前缀和,利用上面异或的性质4,最终不是prefix[r]-prefix[l-1],而是prefix[r]^prefix[l-1],可以得90分
(2)优化:异或为位运算,考虑按位运算优化,考虑每一位0或1对当前位的贡献度(只有0^1=1).题目条件0-20位,对于每一位j,对于prefix[i],若prefix[i]=0,则算前面有多少个1,加上这个个数,就是这个0对这一位的贡献度,最终的和就表示有多少组0和1配对,就是当前位所有异或产生1的情况,然后次数乘上当前位的权重(cnt*(1<<j))
代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N=1e5+10;
ll a[N],n,prefix[N];
ll res;

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i];
	for(int i=1;i<=n;i++){
		prefix[i]=prefix[i-1]^a[i];
	}
	/*
	for(int l=1;l<=n;l++){
		for(int r=l;r<=n;r++){
			res+=prefix[r]^prefix[l-1];
		}
	}
	*/
	for(int j=0;j<=20;j++){ //A_i<=2^20
		int cnt0=0,cnt1=0; //0出现个数,1出现个数 
		ll cnt=0;
		for(int i=0;i<=n;i++){
			if( (prefix[i]>>j) & 1 ){ //当前位为1,cnt加上前面0的个数,1出现个数++ 
				cnt+=cnt0;
				cnt1++;
			}          
			else{
				cnt+=cnt1;
				cnt0++;
			} 						  //当前位为0,cnt加上前面1的个数,0出现个数++
		}
		res+=(ll)cnt*(1<<j);		  //当前位的贡献度*当前位权重 
	}
	cout<<res;
	return 0;
}