1. 程式人生 > >位運算簡介以及常用技巧

位運算簡介以及常用技巧

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\) 計算就好了