位元

建國中學 游承曦

十進位

  • 正常人使用數字的進位方式
  • 遇到 \(10\) 就進位
  • \(817_{(10)} = 8 \times 10^2 + 1 \times 10^1 + 7 \times 10^0\)
  • \(7122_{(10)} = 7 \times 10^3 + 1 \times 10^2  + 2 \times 10^1 + 2 \times 10^0\)

二進位

  • 在電腦中,每個位元只能存 \(0\) 或 \(1\)
  • 一樣遇到 \(2\) 就進位
  • \(1010_{(2)} = 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 0 \times 2^0 = 10_{(10)}\)

十進位轉二進位

  • 可以用短除法來取得 (不斷除以\(2\))

\(\to 18_{(10)} = 10010_{(2)}\)

十進位轉二進位

二進位制轉換

暖身食酢題

位元運算

  • C++中的另一種運算方式、運算子
  • 比十進位運算還要快 (很多)

位元運算子

  • &:And 運算子
  • |:Or 運算子
  • ^:Xor 運算子
  • ~:Not 運算子
  • <<:左移運算子
  • >>:右移運算子

位元運算子的優先順序很低,記得加括號使用!

& And 運算

  • 若兩數同一位元皆為 \(1\),則該位元運算結果為 \(1\)
  • 反之為 \(0\)

| Or 運算

  • 若兩數同一位元其中有一為 \(1\),則該位元運算結果為 \(1\)
  • 反之為 \(0\)

^ Xor 運算

  • 若兩數同一位元不一樣,則該位元運算結果為 \(1\)
  • 反之為 \(0\)

^ Xor 運算

  • Xor 的符號通常使用 \(\oplus\)
  • Xor 的重要性質:
    • \(x \oplus 0 = x\)
    • \(x \oplus x = 0\)
    • \(a \oplus b \oplus b = a \oplus (b \oplus b) = a \oplus 0 = a\)

~ Not 運算

  • 反轉整條位元
  • 特別例子: 在C++中,~\((-1_{(10)}) = 0_{(10)}\)

<< Left Shift 左移運算

  • 將整條位元向左移 \(n\) 位,後面補\(0\)
  • 每左移一位等價於將原數\(\times 2\)
  • \(1\) << \(n\) ?
  • \(2^n\) !

*以非負整數為主

>> Right Shift 右移運算

  • 將整條位元向右移 \(n\) 位
  • 每右移一位等價於 \(x\) := \(\lfloor \frac{x}{2} \rfloor\) (將原數除以二並無條件捨去)

*以非負整數為主

賦值懶人寫法

  • &=
  • |=
  • ^=
  • <<=
  • >>=
  • 沒有~=

簡單蠣鵜

給你一個正整數 \(x\),問把 \(x\) 變成 \(x+1\) 在二進位下需要進位幾次?

Hint: 

後綴 \(1\) 數量

位元運算之進位篇

Problem C. 好多燈泡

給你 \(n\) 個數字,其中只有一個數出現的次數為奇數,找出那個數?

\(n \leq 10^5, \ 0 \leq x_i\) \(\leq 2^{31}\)

\(\mathcal{O}(n)\) Hint: 

Xor

2021 TOI 入營考 pA

給你 \(n\) 個數字 \(x\),你需要把他們排序好,而排序準則是先照 \(popcount(x)\) 由小到大排,若一樣則照原始順序輸出

\(n \leq 10^5, \ 0 \leq x \leq 2^{30}\)

\(popcount(x) : x\) 在二進位下有幾個 bit 為 \(1\)

Problem A 和諧的共鳴頻率

給你 \(n\) 個數 \(a_i\) 和 \(q\) 次詢問

每次詢問區間 \([a_l, ...,  a_r]\) Xor 起來的值

\(n, q \leq 10^6, 0 \leq a_i \leq 10^{18}\)

Collecting Beans

桌面上有許多堆豆子和一個筒子。
現在進行一種遊戲:每次找一堆豆子,如果這堆豆子只有1個,就將它放入筒子,
如果多於一個,則將它分成盡量多而且相等的兩堆,並且把剩下的一個(如果有的話)丟掉。
重複進行許多次之後,所有的豆子要不是在筒子內就是被丟掉了。
現在,我們想要知道在遊戲中會有多少豆子被放入筒子。

\(n \leq 100; a_i \leq 10^7\)

插播:快速冪

如果要求 \(x^n\) ?

\(x\) 乘 \(n\) 次會不會太慢 ?

\(n=10^9\) ? 

解法一:遞迴分治

\(x^n\)

求出 \(x^{\lfloor \frac{n}{2} \rfloor}\) 後再平方就是 \(x^n\),

若 \(n\) 是奇數,再乘上一次 \(x\) 就好了!

複雜度 \(\mathcal{O}(\log n)\)

\(x^{\lfloor \frac{n}{2} \rfloor}\)

\(x^{\lfloor \frac{n}{2} \rfloor}\)

int64_t power(int64_t x, int64_t n){

    if(n == 0) return 1;
    if(n == 1) return x;
    
    int64_t res = power(x, n / 2);
    res = res * res;
    if(n % 2 == 1) res *= x;

    return res;
}

解法二:迭代快速冪

\(x^n = x^1 \times x^2 \times x^4 \times x^8 \times x^{16} \times ...\)

e.g. \(3^{19} = (1 \times 3^1) \times (1 \times 3^2) \times (0 \times 3^4) \times (0 \times 3^8) \times (1 \times 3^{16})\)

一邊做 \(3\) 的自乘一邊算答案!

\(\because 19_{(10)} = 10011_{(2)}\)

int64_t power(int64_t x, int64_t n){

    int64_t res = 1;
    
    while(n > 0){
        if(n % 2 == 1) res *= x;
        x *= x;
        n /= 2;
    }

    return res;
}
int64_t power(int64_t x, int64_t n){

    int64_t res = 1;
    
    while(n){
        if(n & 1) res *= x;
        x *= x;
        n >>= 1;
    }

    return res;
}

蠻噁的位元運算寫法

簡單乘法

求 \(a \times b \bmod m\)

\(0 \leq a, b \leq 10^{18}, \ 1 \leq m \leq 10^{18}\)

盡量不要用 python :p

枚舉子集合

一個簡單的題目:

給你 \(n \leq 20\) 個數字,

問你有沒有辦法選一些數字使其總和為 \(k\) ?

考慮一個純粹暴力的解法:

枚舉每一個數要選或不要選

數學上的集合

  • 集合為一個包含多個元素的群聚體
  • 如果有兩個集合 \(A, S\),且 \(A \subseteq S\),則 \(A\) 為 \(S\) 的子集合
  • e.g. 
    • \(S = \{1, 2, 3, 4, 5\}\)
    • \(A = \{2, 4\}\)
  • 我們可以用一個整數 \(n\) 的二進位來表示一個集合的狀態
  • 一個 \(n\) 個元素的集合有 \(2^n\) 個子集合 (含空集合\(\phi\))

以二進位表示集合

  • 用 \(n\) 個位元的布林值來表示集合 \(S\) 的狀態
  • 第 \(i\) 個位元為 \(1\) 代表 \(a_i \in S\)
  • 在 \(n \leq 20 \) 時適用一個非負整數來表示一個集合
  • 在 dp (動態規劃) 裡常作為 狀態壓縮 的工具

栗子

  • \(n = 19_{(10)} = 10011_{(2)}\)

  • 則 \(S = \{a_0, a_1, a_4\}\)

  • \(\mid S \mid = 3\)

集合的運算 (以 int 為例)

  • 有兩個集合 \(A, B\)

  • \(A \land B : A \& B\)

  • \(A \lor B : A \mid B\)

  • 判斷第 \(i\) 個元素是不是在集合 \(A\) 裡: A & (1<<i) 或 (A >> i) & 1

  • 包含所有元素的集合: (1<<n)-1

回到剛剛的問題

  • 定義宇集 \(S = \{a_i \mid 0 \leq i < n\}\)
  • 枚舉 \(S\) 的所有子集合,計算該集合內所有元素的和
  • 只要有一總和為 \(k\) 就是 Yes,反之 No

枚舉子集合的寫法

int n, a[20];
int k;
bool found = false;
for(int s=1 ; s<(1<<n) ; ++s){
    int sum = 0;
    for(int i=0 ; i<n ; ++i){
        if(s & (1<<i)){
            sum += a[i];
        }
    }
    if(sum == k){
        found = true;
    }
}

Rejudge!

小複雜的枚舉子集題目

毒瘤的東西

  • #include <algorithm>
  • __lg(x) : \(\lfloor \log_2 x \rfloor\)
  • __builtin_popcount(x) : \(x\) 在二進位下 bit 為 \(1\) 的數量
  • __builtin_clz(x)

更多更多的題

Logical Expression

Rock and Lever

給你 \(n\) 個數 \(a_i\),

問有幾組 \((i, j)\) 符合 \(i < j\) 且 \(a_i \& a_j \geq a_i \oplus a_j\) ?

\(n \leq 10^5\)

Apollo versus Pan

給你 \(n\) 個數 \(x_i\),

求 \(\displaystyle \sum_{i=1}^n \sum_{j=1}^n \sum_{k=1}^n (x_i \& x_j) \cdot (x_j \mid x_k)\) ?

\(n \leq 5 \times 10^5\)

Genius's Gambit

給你 \(a, b, k\)

構造兩個 0-1 字串 \(x, y\) 各有 \(a\) 個 0 和 \(b\) 個 1 (不能有前綴 0)

且 \(x-y\) 有 \(k\) 個 1

\(0 \leq a; 1 \leq b; 0 \leq k \leq a+b \leq 2 \times 10^5\)

位元DP

旅行者_九國遊歷記<4> 小綠去焱國

C-Game

鍊金術

位元

By youou

位元

  • 371