位運算簡介以及常用技巧
阿新 • • 發佈:2019-08-27
A.什麼是位運算 ?
計算機裡的記憶體都是用 二進位制 儲存的,說白了位運算就是對這些 二進位制數 去操作
由於是直接對 二進位制數 去進行操作,就會有許多優秀的性質.
一般來說有這麼幾個常用的 位運算 符號 :
位運算子號 名稱 規則 例子 & 與運算子 相同位的兩個數字都為 \(1\) 則為 \(1\); 若有一個不為 \(1\), 則為 \(0\) 1100 & 1010 = 1000 | 或運算子 相同位要有一個為 1 則為 1 1100 | 1010 = 1110 ^ 異或運算子 相同位不同則為 \(1\), 相同則為 \(0\) 1100 ^ 1010 = 0110 ~ 取反運算子 \(0\) 和 \(1\) 全部取反 ~1001 = 0110 << 左移運算 x << n 相當與 \(x \times 2 ^ n\) 101 << 2 = 10100 >> 右移運算 x >> n 相當於 \(x / 2 ^ n\) 1010 >> 2 = 10
B.二進位制列舉
0. 代替一類 0/1 dfs
一個二進位制數的 \(0/1\) 可以代表某一個元素選與不選 既然這樣我們只要列舉一個二進位制數就可以代替遞迴版本的 dfs 這樣做雖然可以避免遞迴操作的較大常數 但是我們只得到了一個二進位制數,我們還要把它用 \(O(logn)\) 的時間複雜度把它解碼,emmm...反正看情況哪個方便用哪個唄qwq
1. 列舉子集
for(int i = S; i; i = i - 1 & S)
2. 列舉包含指定元素的集合
for(int i = S; i; i = i + 1 | S)
3. 列舉指定個數元素的集合*(常數有點大)
int x, b, t, c, m, r; x = BinRd(); b = x & -x; t = x + b; c = t ^ x; m = (c >> 2) / b; r = t | m;
C.一種位運算的字首求和
1.求:
\[ \sum_{i = 1}^n\sum_{j =1}^iA_i xor A_j \]
可以去處理陣列 \(pre[i][32][0 / 1]\) 表示 \(i\) 之前的數某一位上有多少個 \(0 / 1\)每一位分別 \(logn\) 計算答案,\(logn\) 修改 字首 \(pre\) 陣列
假如把 \(xor\) 換成 \(and\) 的話只要計算某一位上有多少個 \(1\) 就好了,可以少一維
2.求:
\[ \sum_{i < j < k} (A_ixorA_j) \times (A_jxorA_k) \]
發現 \(j\) 在中間,那麼我們求一個 \(bit[32][0 / 1]\) 字首和,求一個 \(bit[32][0 / 1]\) 字尾和列舉每一個位置 \(logn\) 計算就好了