核心思想
通过二进制特性,判断一个数是否仅有一个二进制位是1。因为2的幂次数的二进制形式满足这一条件(例如:1 (1)
, 2 (10)
, 4 (100)
, 8 (1000)
等)。
关键操作:k & (k - 1)
操作步骤
-
计算
k-1
:
当k
的二进制最低位的1
变为0
,之后所有低位变为1
。
示例:-
k = 8 (1000)
→k-1 = 7 (0111)
-
k = 5 (101)
→k-1 = 4 (100)
-
-
按位与运算
k & (k-1)
:
将k
和k-1
的二进制逐位进行与操作。-
如果
k
是2的幂次,则结果为0
。 -
如果
k
不是2的幂次,则结果不为0
。
-
数学证明
情况1:k
是2的幂次
-
二进制形式:
k
的二进制仅有一个1
(例如1000
)。 -
计算
k-1
:该操作会将k
的二进制中唯一的1
变为0
,之后所有低位变为1
(例如1000 → 0111
)。 -
按位与结果:
1000 (k) & 0111 (k-1) ---------- 0000
结果为
0
,满足条件。
情况2:k
不是2的幂次
-
二进制形式:
k
的二进制中有多个1
(例如1010
)。 -
计算
k-1
:仅影响最低位的1
,其他高位保持不变(例如1010 → 1001
)。 -
按位与结果:
1010 (k) & 1001 (k-1) ---------- 1000
结果不为
0
,不满足条件。
边界条件处理
-
k = 0
:-
0
不是2的幂次,但0 & (-1) = 0
。需额外判断k > 0
。
-
-
k = 1
:-
1
是2⁰,满足条件。 -
1 & 0 = 0
,结果正确。
-
代码实现示例
bool isPowerOfTwo(int k) { return (k > 0) && ((k & (k - 1)) == 0); }
优势分析
-
时间复杂度:
O(1)
,仅需一次减法和按位与操作。 -
无依赖:无需特定库函数(如
__builtin_popcount
),跨平台兼容性强。 -
高效性:位运算直接操作内存,速度快于循环或数学函数。
应用
蓝桥杯入门赛题目-----打花结
问题描述
打花结是一种中国传统手工艺,也被称为“中国结”或“结绳艺术”。它是一种用绳子编织而成的装饰品,常见于节庆、婚礼、生日等重要场合。打花结的形状多样,有圆形、方形、菱形等,每种形状都有其独特的寓意和象征意义。
小蓝最近得到了 NN 张矩形花纸。每张花纸的长为 lili,宽为 wiwi。小蓝想要从中选取一些花纸来制作打花结,但只有经过若干次折叠操作后使花纸的面积变为 XX 的花纸才能用于打花结。
折叠操作如下:
-
将纸准确对折成一半,形成一条与宽度方向平行的水平对折线,长度减半。
-
将纸准确对折成一半,形成一条与长度方向平行的垂直对折线,宽度减半。
请你判断每张花纸是否适合打花结,如果适合,则输出 Yes
;否则输出 No
。
输入格式
第一行输入两个整数 N,X(1≤N≤103,1≤X≤104)N,X(1≤N≤103,1≤X≤104) 表示花纸数量。
接下来 NN 行,每行输入两个整数 li,wi(1≤li,wi≤100)li,wi(1≤li,wi≤100) 表示花纸的长宽。
输出格式
输出 NN 行,若第 ii 张花纸适合打花结则输出 Yes
,否则输出 No
。
样例输入
3 10
2 5
10 8
3 10
样例输出
Yes
Yes
No
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 256M |
C | 1s | 256M |
Java | 2s | 256M |
Python3 | 3s | 256M |
PyPy3 | 3s | 256M |
Go | 3s | 256M |
JavaScript | 3s | 256M |
代码运用
#include <iostream>
using namespace std;
int main() {
int n, x;
cin >> n >> x;
while (n--) {
int l, w;
cin >> l >> w;
if (x == 0) {
cout << "No\n";
continue;
}
long long area = (long long)l * w;
if (area % x != 0) {
cout << "No\n";
continue;
}
long long k = area / x;
if (k > 0 && (k & (k - 1)) == 0) {
cout << "Yes\n";
} else {
cout << "No\n";
}
}
return 0;
}
代码解释
代码结构概览
这段代码用于判断多个矩形是否能被分割成指定面积(x
)的瓷砖,且瓷砖数量必须是 2的幂次(如1,2,4,8等)。
逐行解析
int main() { int n, x; cin >> n >> x;
-
功能:
-
读取测试用例数量
n
和单块瓷砖面积x
。
-
while (n--) { int l, w; cin >> l >> w;
-
功能:
-
循环处理每个矩形,读取其长度
l
和宽度w
。
-
// 处理 x=0 的情况 if (x == 0) { cout << "No\n"; continue; }
-
关键点:
-
避免除零错误。若
x=0
,无法定义瓷砖面积,直接输出No
。
-
// 防止整数溢出 long long area = (long long)l * w;
-
关键点:
-
将
l
和w
强制转换为long long
再相乘,防止乘积溢出(例如l=1e5
,w=1e5
时,int
会溢出)。
-
// 检查面积是否能被x整除 if (area % x != 0) { cout << "No\n"; continue; }
-
逻辑:
-
若总面积
area
无法被x
整除,说明无法用完整瓷砖铺满,直接输出No
。
-
long long k = area / x;
-
功能:
-
计算需要的瓷砖数量
k
。
-
// 检查k是否为正数且是2的幂次 if (k > 0 && (k & (k - 1)) == 0) { cout << "Yes\n"; } else { cout << "No\n"; }
-
核心逻辑:
-
条件
k > 0
:-
排除
k=0
(面积不足)或k<0
(面积或瓷砖面积为负数)的情况。
-
-
位运算
(k & (k-1)) == 0
:-
原理:若
k
是2的幂次,其二进制表示仅有一个1
。-
例如
k=8 (1000)
,k-1=7 (0111)
,按位与结果为0
。
-
-
意义:高效判断
k
是否为2的幂次,时间复杂度O(1)
。
-
-
边界条件处理
-
x=0
:-
直接输出
No
,避免除零错误。
-
-
整数溢出:
-
使用
long long
存储面积,防止l*w
溢出。
-
-
k<=0
:-
若
k
非正数(如x
为负数或面积不足),直接输出No
。
-
代码功能性对比
#include <iostream>
using namespace std;
int main() {
int n,x;
cin>>n>>x;
while (n--) {
int l,w;
cin>>l>>w;
cout<<(l*w%x == 0 && __builtin_popcount(l*w/x) == 1 ? "Yes" : "No")<<'\n';
}
return 0;
}
看似简洁实际既不利于理解和使用,而且调用_builtin_popcount库,复杂度大大提升
写者笔记