內容
給你兩個整數 a,b
求 a 到 b 之間所有整數(含)進行 and 運算的結果
輸入
每一行兩個非負整數 a,b(a,b<2^64)
12 15
2 3
8 13
17 23
11 15
輸出
答案
12
2
8
16
8
解題思路
很困難也很簡單的一題,重點在「進位」,二進制在進位之後原本的 1 都會變成 0,像是
0111(2) 進位後就會變成 1000(2),而題目是使用 AND 運算,當某位元出現過 0 之後不管之後出現幾次 1 都不會變回 1,因此當範圍內有任一 8 或其倍數(?????000(2)) 時代表答案的最後 3 位必為 0 ,同理能套用在所有 2 的乘冪或其倍數。(如下表)
數 |
二進制 |
2 |
??????0 |
4 |
?????00 |
8 |
????000 |
16 |
???0000 |
32 |
??00000 |
64 |
?000000 |
而既然二的冪次或其倍數一定是最多 0 的,那在 a ~ b 中最大的二的冪次或其倍數就會是答案了,所以接下來的目標就是求出範圍內最大的二的冪次或其倍數,這時候就要用到下式
n = n & (n - 1);
這個式子可以把任一數的最後一位 1 變成 0 ,像是
11000(2) & 10111(2) = 10000(2)
11100(2) & 11011(2) = 11000(2)
11010(2) & 11001(2) = 11000(2)
11001(2) & 11000(2) = 11000(2)
具體原理是因為借位會讓最小位的 1 變成 0,讓比最小位 1 還小的 0 變成 1 ,但 0 和 1 經過 位元 AND 運算後又都會變回 0 ,所以就能達成將最小位的 1 改為 0 的效果。
用這個式子不斷的移除 b 的最小位 1 ,直到 b <= a ,這時候的 b 就會是範圍內最大 2 的冪次的倍數,也就是答案了。
注意本題中輸入的 a 有可能比 b 還大,所以要先判斷兩數大小。
完整程式碼
AC (14ms, 1.1MB)
#include <stdio.h> #define BUFSIZ 1048576 #define SWAP(x, y) (x)^=((y)^=((x)^=(y)))
inline char readChar() { static char buffer[BUFSIZ], * now = buffer + BUFSIZ, * end = buffer + BUFSIZ; if (now == end) { if (end < buffer + BUFSIZ) return EOF; end = (buffer + fread(buffer, 1, BUFSIZ, stdin)); now = buffer; } return *now++; }
inline char readULongLong(unsigned long long* dst) { register char ch; while ((ch = readChar()) < '0') if (ch == EOF) return 0; *dst = ch ^ '0'; while ((ch = readChar()) >= '0') * dst = (*dst << 3) + (*dst << 1) + (ch ^ '0'); return 1; }
inline void putULongLong(register unsigned long long src, const char suffix) { register unsigned long long div; char tmp[22], * st = tmp + 20; *st = suffix, * (st + 1) = 0; do { *(--st) = src - ((div = src / 10) << 3) - (div << 1) + '0'; } while (src = div); fputs(st , stdout); }
int main() { unsigned long long f, t; while (readULongLong(&f), readULongLong(&t)) { if (t < f) SWAP(t, f); while (f < t) t &= (t - 1); putULongLong(t, '\n'); } return 0; }
|