Files
python/TangDou/AcWing/BIT/801.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

190 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## [$AcWing801$. 二进制中$1$的个数 ](https://www.acwing.com/problem/content/description/803/)
### 一、题目描述
给定一个长度为 $n$ 的数列,请你求出数列中每个数的二进制表示中 $1$ 的个数。
**输入格式**
第一行包含整数 $n$。
第二行包含 $n$ 个整数,表示整个数列。
**输出格式**
共一行,包含 $n$ 个整数,其中的第 $i$ 个数表示数列中的第 $i$ 个数的二进制表示中 $1$ 的个数。
**数据范围**
$1≤n≤100000,0≤数列中元素的值≤10^9$
**输入样例**:
```cpp {.line-numbers}
5
1 2 3 4 5
```
**输出样例:**
```cpp {.line-numbers}
1 1 2 1 2
```
### 二、前导知识
位运算中有**两个重要性质**:
#### 一、$x\&(-x)$
#### 保留二进制下**最后出现**的$1$的位置,其余位置置$0$
演示一下 $x\&(-x)$ 的计算过程:
对于 $x = 10$ (二进制:$00001010$):
1. 求 $-x$ 的过程:
```
x = 00001010
取反(~x) = 11110101
加1 = 11110110 (-x的最终值)
x&(-x) = 00001010
& 11110110
= 00000010 (结果)
```
$x = 10$ 的二进制表示中,最右边的$1$在第$2$位(从右往左数,从$1$开始) $x\&(-x) = 2$,正好是这个位置对应的值。
封装函数:
```cpp {.line-numbers}
int lowbit(int x) {
return x & (-x);
}
```
#### 二、$x\&(x-1)$
#### 消除二进制下**最后出现**$1$的位置,其余保持不变
假设 $x = 52$,其二进制表示为:
```
x = 52 = 00110100
x-1 = 51 = 00110011
x & (x-1) = 00110000 = 48
```
**工作原理**:
当我们将一个数减$1$时,从最右边的$1$开始,这一位变成0,而它右边的所有$0$都变成$1$
当我们将原数和减$1$后的数做与运算时,最右边的$1$及其右边的所有位都会变成$0$,而左边的位保持不变
这就是为什么这个技巧常用于:
- 计算二进制中$1$的个数(每操作一次消除一个$1$)
- 判断一个数是否是$2$的幂(如果是$2$的幂,执行一次后就变成$0$)
```c++
while (x) {
x = x & (x - 1); // 每次操作消除最右边的1
cnt++; // 记录操作次数,就是1的个数
}
```
这个方法比直接遍历$32$位要高效,因为它的循环次数等于二进制中$1$的个数,而不是固定的$32$次。
### 三、练习$x\&(x-1)$
#### 求下面函数的返回值 (微软)
```cpp {.line-numbers}
int func(x) {
int countx = 0;
while(x){
countx ++;
x = x & (x-1);
}
return countx;
}
```
功能:将$x$转化为$2$进制,看含有的$1$的个数。
注: 每执行一次$x = x\&(x-1)$,会将$x$用二进制表示时最右边的一个$1$变为$0$,因为$x-1$将会将该位($x$用二进制表示时最右边的一个$1$)变为$0$。
#### 判断一个数$x$是否是$2$的$n$次方
```cpp {.line-numbers}
#include <bits/stdc++.h>
int func(int x){
if((x & (x-1)) == 0)
return 1;
else
return 0;
}
int main(){
int x = 8;
printf("%d\n", func(x));
return 0;
}
```
**思路**
如果一个数是$2$的$n$次方,那么这个数用二进制表示时其最高位为$1$,其余位为$0$。
### 四、利用 $x\&-x$
```cpp {.line-numbers}
#include <iostream>
using namespace std;
int lowbit(int x) {
return x & (-x);
}
int main() {
int n;
cin >> n;
while (n--) {
int x;
cin >> x;
int res = 0;
while (x) x -= lowbit(x), res++;
printf("%d ", res);
}
return 0;
}
```
### 五、利用 $x \& (x-1)$
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
int cnt = 0;
int x;
cin >> x;
while (x) {
x = x & (x - 1);
cnt++;
}
printf("%d ",cnt);
}
return 0;
}
```
### 六、遍历每一个二进制位
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
int cnt = 0;
int x;
cin >> x;
for (int i = 0; i < 32; i++)
if (x >> i & 1) cnt++;
cout << cnt << " ";
}
return 0;
}
```