AA競程
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    2
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # USACO 2025 Open Contest AA 競程 dreamoon 老師的參考做法 [TOC] ## Bronze ### [Problem 1. Hoof Paper Scissors Minus One](https://usaco.org/index.php?page=viewproblem&cpid=1503) #### 題目敘述 在遊戲 Hoof Paper Scissors 中,Bessie 與 Elsie 各自從 $N$ 個不同的手勢(蹄式)中選一個(編號為 $1$ 到 $N$),並根據一張規則表,不同手勢間比賽時會有兩種結果: * 一方勝出、另一方失敗 * 平手 而在 Hoof Paper Scissors Minus One 中,兩隻牛各自同時出示兩個手勢(左蹄一個、右蹄一個)。各自手勢都被對方看見後,他們再從自己的兩個手勢中選一個作為正式比賽所出的手勢,最後依照規則表決定勝負。 給定 Elsie 在每場比賽中預計要出的手勢組合(共 $M$ 場比賽),Bessie 想知道自己有多少種手勢組合可以保證一定能贏過 Elsie。這裡「手勢組合」指的是一個有序對 $(L, R)$,其中 $L$ 為左蹄的手勢,$R$ 為右蹄的手勢。 #### 輸入格式 第一行包含兩個空格分隔的整數: * $N$:象徵總數 ($1 \le N \le 3000$) * $M$:比賽場數 ($1 \le M \le 3000$) 接下來 $N$ 行: * 第 $i$ 行包含 $i$ 個字元:$a_{i,1}a_{i,2}\dots a_{i,i}$,每個字元皆為以下三者之一: * D:象徵 $i$ 與象徵 $j$ 平手 * W:象徵 $i$ 勝過象徵 $j$ * L:象徵 $i$ 輸給象徵 $j$ * 保證 $a_{i,i} = \texttt{D}$。 接下來 $M$ 行: * 每行包含兩個空格分隔的整數 $s_1$ 與 $s_2$,表示 Elsie 本場比賽出的象徵組合。 #### 輸出格式 輸出 $M$ 行,每行包含一個整數代表在第 $i$ 場比賽 Bessie 有幾種手勢組合可以保證一定贏過 Elsie。 #### 測資額外限制 測資 2~6:$N, M \le 100$ 測資 7~12:無額外限制 #### 參考做法 > 此題用到了 AA 競程 Level 1 的「競程數學」中的知識點。 假設 Bessie 出的左蹄和右蹄手勢編號分別為 $a$ 和 $b$,那麼 Bessie 一定能贏的情況就是 $a$ 同時贏過 $s_1$ 和 $s_2$ 或 $b$ 同時贏過 $s_1$ 和 $s_2$,用 C++ 的程式語法來寫就是: ```cpp (s[a][s1] == 'W' && s[a][s2] == 'W') || (s[b][s1] == 'W' && s[b][s2] == 'W') ``` 暴力枚舉 $a$ 和 $b$ 即可通過前 6 筆測資。 若要通過所有測資就要用點數學方法來計算 Bessie 一定能贏的組合數量。 首先,我們還是要先暴力計算有多少手勢是能同時贏過 $s_1$ 和 $s_2$ 的,令此數量為 $cnt$。 接著 Bessie 能贏的數量其實就是「全部可能的手勢組合數量」減去「不能贏的組合數量」 「全部的組合數量」可用乘法原理算出是 $N \times N$,而不能贏的組合數量就是 $a$ 和 $b$ 都不是那 $cnt$ 個能贏的手勢的組合,也能利用乘法原理算出此數樣為 $(N - cnt) \times (N - cnt)$,所以答案就是 $N \times N - (N - cnt) \times (N - cnt)$ #### 參考程式碼 ```cpp= #include <iostream> #include <vector> #define SZ(X) (int)(X).size() using namespace std; const int SIZE = 5000; int main() { cin.tie(0); ios_base::sync_with_stdio(0); int N, M; cin >> N >> M; vector<vector<char>> c(N + 1, vector<char>(N + 1)); for (int i = 1; i <= N; i++) { for (int j = 1; j <= i; j++) { cin >> c[i][j]; if (c[i][j] == 'D') { c[j][i] = 'D'; } else if (c[i][j] == 'W') { c[j][i] = 'L'; } else { c[j][i] = 'W'; } } } while (M--) { int x, y; cin >> x >> y; int cnt = 0; for (int i = 0; i < N; i++) { if (c[i][x] == 'W' && c[i][y] == 'W') cnt++; } cout << N * N - (N - cnt) * (N - cnt) << '\n'; } } ``` ### [Problem 2. More Cow Photos](https://usaco.org/index.php?page=viewproblem&cpid=1504) #### 題目敘述 今天牛群特別頑皮! Farmer John(FJ)只想拍下排成一行的牛,但牠們總在快要拍照時移動。 具體來說,FJ 有 $N$ 隻牛 ($1 \le N \le 10^5$),每隻牛都有一個介於 $1$ 到 $N$ 的整數高度。FJ 希望拍出一張牛的排列照片,要求牛的高度從左到右必須滿足以下三個條件: 1. **山形排列**:牛的高度必須先不降後不升。也就是說,存在一個位置 $i$ 使得 $h_1 \le h_2 \le \dots \le h_i \ge h_{i+1} \ge \dots \ge h_K.$ 2. **相鄰不等**:相鄰的兩頭牛高度必須不同,即對所有 $1 \le i < K$ 有 $h_i \ne h_{i+1}$。 3. **對稱**:照片必須左右對稱。也就是說,若 $i+j=K+1$,則必須有 $h_i = h_j$。 FJ 可以移除部分牛,並重新排列剩下的牛。請求出 FJ 能夠在照片中包含的牛的最大數量,使得上述條件均被滿足。 #### 輸入格式 輸入包含多個測試案例。 - 第一行包含一個整數 $T$ ($1 \le T \le 10^5$),表示測試案例數量。 - 接下來每個測試案例格式如下: - 第一行:一個整數 $N$,表示牛的數量。 - 第二行:$N$ 個整數,分別表示每頭牛的高度,均介於 $1$ 到 $N$ 之間。 保證所有測試案例中 $N$ 的總和不超過 $10^6$。 #### 輸出格式 輸出 $T$ 行,每一行輸出一個整數,表示對應測試案例中 FJ 能在照片中包含的牛的最大數量。 #### 測試資料額外限制 - 測資 2-3:$T \le 100$, $N \le 7$ - 測資 4-5:$T \le 10^4$,所有牛的高度均不超過 $10$ - 測資 6-11:無額外限制 #### 參考做法 > 此題的參考做法用到了 AA 競程 Level 0 競程入門班的「用陣列紀錄資訊」單元以及 Level 1 的「簡易貪心」單元中的知識點。 先最高的牛的高度,以及用陣列把每個高度的牛各有幾隻算出來。 由於牛的排列必須是「山形排列」,且相鄰的兩隻牛又不能一樣,那把牛的身高列出來可能會是如下: > 1 2 4 7 9 7 4 2 1 可發現除了最大的數字外,其他數字都是恰出現兩次。 既然我們要找的是最長的排列,那麼我們讓身高最大的牛站在正中央一定能找到解,令身高最大的牛高度為 $max\_h$,那麼對於所有 $i = 1 \sim max_h - 1$,只要身高 $i$ 的牛出現兩隻以上,我們就一定可以選其中兩隻出現在照片中,所以答案就是: $$ \sum\limits_{i=1}^{max\_h-1} 2 \times [cnt[i] \ge 2] $$ 其中 $cnt[i]$ 代表身高為 $i$ 的牛有幾隻,$[\ ]$ 為 [艾佛森括號](https://zh.wikipedia.org/zh-tw/%E8%89%BE%E4%BD%9B%E6%A3%AE%E6%8B%AC%E8%99%9F) #### 參考程式碼 ```cpp= #include <iostream> #include <vector> #define SZ(X) (int)(X).size() using namespace std; void solve() { int N; cin >> N; vector<int> cnt(N + 1); int max_h = 0; for (int i = 1; i <= N; i++) { int x; cin >> x; if (max_h < x) max_h = x; cnt[x]++; } int ans = 1; for (int i = 1; i < max_h; i++) { if (cnt[i] >= 2) ans += 2; } cout << ans << '\n'; } int main() { cin.tie(0); ios_base::sync_with_stdio(0); int t; cin >> t; while (t--) solve(); } ``` ### [Problem 3: It's Mooin' Time III](https://usaco.org/index.php?page=viewproblem&cpid=1505) #### 題目敘述 Elsie 正試圖向 Bessie 描述她最喜愛的 USACO 競賽,但 Bessie 卻無法理解 Elsie 為何如此喜歡這個比賽。Elsie 說:「而且到了 mooin' time!誰想要來個 mooin'? 拜託,我只想參加 USACO。」 Bessie 仍然不懂,所以她將 Elsie 的描述記錄成一個長度為 $N$ ($3 \leq N \leq 10^5$) 的字串,字串由小寫英文字母組成,記作 $s_1 s_2 \ldots s_N$。Elsie 定義一個由三個字元組成的字串 $t$ 為 moo,當且僅當 $t_2 = t_3$ 且 $t_2 \neq t_1$。 一個三元組 $(i,j,k)$ 若滿足 $i < j < k$ 且字串 $s_i s_j s_k$ 構成一個 moo,則稱該三元組有效。對於一個有效三元組,FJ 會進行以下操作以計算其值: - FJ 將字串 $s$ 在索引 $j$ 處旋轉 $90$ 度。 - 該三元組的值為三角形 $\Delta ijk$ 面積的兩倍。 換句話說,三元組的值為 $(j-i)(k-j)$。 Bessie 向你提出 $Q$ ($1 \leq Q \leq 3 \cdot 10^4$) 個查詢。在每個查詢中,她給出兩個整數 $l$ 與 $r$ ($1 \leq l \leq r \leq N$, 且 $r-l+1 \geq 3$),要求你求出所有滿足 $l \leq i$ 且 $k \leq r$ 的有效三元組 $(i,j,k)$ 中,其值的最大值。如果不存在任何有效三元組,請輸出 $-1$。 <strong>注意:</strong> 此題中涉及到的整數數值可能非常大,因此在程式中可能需要使用 64 位整數型態(例如 C/C++ 中的 "long long")。 #### 輸入格式 - 第一行包含兩個整數 $N$ 與 $Q$。 - 接下來一行包含字串 $s_1 s_2 \ldots s_N$。 - 接下來 $Q$ 行,每行包含兩個整數 $l$ 與 $r$,表示一次查詢。 #### 輸出格式 對每個查詢,輸出一行答案,即該查詢中最大有效三元組的值;若不存在有效三元組,輸出 $-1$。 #### 參考做法 > 此題的參考做法用到了 AA 競程 Level 1「簡易貪心」、「數列相關函式」單元以及 「Level 1 公開課」中的知識點。 首先對於每個詢問我們要枚舉三元組的第二第三個元素是哪個字元,只有 $26$ 種可能,每種可能各自算出最佳解後,再取最大的那個就是答案了。 接下來我們假設第二第三個元素對應的字母是 $c$。 首先我們可以用貪心的概念來縮小我們要考慮的三元組的數量,最後可發現對於每個 $c$,其實我們至多只要考慮 $2$ 個三元組,解説如下: * 在知道在確定第二第三個字母是 $c$ 的情況,第一個字母一定是選區間 $[l, r]$ 中最左邊的不是 $c$ 的字母最好(假設此位置為 $first\_pos$),最後一個字母則是選區間中最右邊的 $c$(假設次位置為 $last\_pos$)。而第二個字母一定是選越靠近 $mid = (first\_pos + last\_pos)/2$ 的位置越好,最靠近 $mid$ 的候補為只有兩個: $\ge mid$ 的最左邊位置以及 $< mid$ 的最右邊位置。 而 $first\_pos$ 的位置可讀入字串後,就先預處理 $nextDiff$ 陣列,$nextDiff[pos]$ 代表位置 $pos$ 右邊第一個和 $s[pos]$ 不一樣的字母位置,$nextDiff$ 陣列求法請參考範例程式碼。 如此一來,對於如果 $s[l]$ 不是 $c$,那麼 $first\_pos$ 就是 $l$,否則 $first\_pos$ 就是 $nextDiff[l]$。 而 $last\_pos$ 的求法可是先把所有字元 $c$ 的位置放入一個 vector 裡,然後使用 $upper\_bound$ 來求。 最後,靠近 $mid$ 的位置也可使用 $lower\_bound$ 函式來求出,於是此題的時間複雜度分析如下: * 預處理 $nextDiff$ 陣列的時間為 $O(N)$ * 回答每個詢問的時間為 $O(字母集大小 \times \log{N})$ * 總時間複雜度為 $O(N + 字母集大小 \times \log{N})$ #### 參考程式碼 ```cpp= #include <algorithm> #include <iostream> #include <vector> using namespace std; #define SZ(X) (X).size() typedef long long ll; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; string s; cin >> s; // 原始字串讀入時為 0-indexed,但我們後續用 1-indexed 處理 s = " " + s + " "; // 預處理:計算 nextDiff 陣列 (1-indexed) vector<int> nextDiff(N + 2, N + 1); // 設定 INF 為 N+1 nextDiff[N] = N + 1; // 最後一個位置無後續不同字元 for (int i = N; i >= 1; i--) { if (s[i] != s[i + 1]) nextDiff[i] = i + 1; else nextDiff[i] = nextDiff[i + 1]; } // 對每個字母建立出現位置陣列(1-indexed) vector<vector<int>> pos(26); for (int i = 1; i <= N; i++) { char c = s[i]; pos[c - 'a'].push_back(i); } // 處理每個查詢 while (Q--) { int l, r; cin >> l >> r; ll ans = -1; // 枚舉每個字母作為三元組第二和第三個位置的值的情況,分別求最佳解 for (char c = 0; c < 26; c++) { // 先求出當第二第三個字元是 c 時, [l, r] 間可作為第一個字元的位置的最左邊的位置 int first_pos = l; if (s[l] == c + 'a') { first_pos = nextDiff[l]; } // 如果第一個字母可能的最左邊位置都大於 r - 2,一定無解 if (first_pos > r - 2) continue; auto &vec = pos[c]; // 利用 upper_bound 求出該三元組第三個元素在 [l, r] 可出現的最右邊的位置,若有解選最右邊的位置一定是最佳解 auto itR = upper_bound(vec.begin(), vec.end(), r); if (itR == vec.begin()) continue; itR--; if (*itR <= first_pos) continue; int last_pos = *itR; // last_pos 為最 [l, r] 內最右邊可能是 c 的位置 int mid = (first_pos + last_pos) / 2; // 最佳解只出現在三元組第二個元素 >= mid 的最小位置或 < mid 的最大位置 int mid_index = lower_bound(vec.begin(), vec.end(), mid) - vec.begin(); for (int j = max(0, mid_index - 1); j <= mid_index && j < SZ(vec); j++) { if (vec[j] > first_pos && vec[j] < last_pos) { ans = max(ans, (vec[j] - first_pos) * 1LL * (last_pos - vec[j])); } } } cout << ans << "\n"; } return 0; } ``` ## Silver ### [Problem 1: Sequence Construction](https://usaco.org/index.php?page=viewproblem&cpid=1506) #### 題目敘述 給你兩個正整數 $M, K$ ($1 \le M \le 10^9, 1 \le K \le 31$),請構造一個非負整數數列 $a_1, a_2, \ldots, a_N$ 滿足以下條件: 1. $1 \le N \le 100$ 2. $\sum\limits_{i=1}^N a_i = M$ 3. $popcount(a_1)\ xor\ popcount(a_2)\ xor\ \ldots\ xor\ popcount(a_N) = K$ ($popcount(x)$ 指的是 $x$ 寫成二進制後,有幾個位數是 $1$, $xor$ 則是按位元互斥或) #### 參考做法 > 此題的參考做法免強算是用到了 AA 競程 Level 1「簡易貪心」吧... 這種莫名其妙的構造題,感覺就是用來防 chatGPT 用的 XD 題目只有兩個參數 $M$ 和 $K$,不妨先固定其中一個參數,想想看另一個參數在什麼時候會有解,尤其時去思考看看有解時該參數的極小極大值會是什麼 例如我們可以固定 $K$,思考看看對於同一個 $K$ 值,$M$ 最小能多小。 由於 $K$ 是所有 $a_i$ 位元 $1$ 的數量 xor 起來的結果,那也就是說,如果 $K$ 二進制最高位是 $h$,那麼無論如何都至少要有一個 $a_i$ 含有的 位元 $1$ 的數量是 $2^h$ 以上了。 這樣想下去大概能發現 $M$ 最小的情況會發生在 所有的 $popcount(a_i)$ 都是 $2$ 的冪次方,且 $popcount(a_1)\ +\ popcount(a_2)\ +\ \ldots\ +\ popcount(a_N) = K$ 的時候,於是我們就能夠貪心地算出給定 $K$ 時,$M$ 的可能最小值,令此值為 $f(K)$。 接著分 $4$ 種情況討論。 1. 當 $M \le f(K)$,無解。 2. 當 $M \ge f(K)$,且 $M - f(K)$ 是偶數時,我們就只要先把輸入為 ``f(K) K`` 的解構造出來,最後數列 $a$ 再加上兩個 $(M - f(k))/2$ 就是答案了。 3. 當 $M \ge f(K)$,且 $M - f(K) = 1$ 時,又有兩種情況: 1. 若輸入為 ``f(K) K`` 的解有某個 $a_i = 1$,那把此 $a_i$ 改成 $2$,此 $a_i$ 的位元 $1$ 數量不會改變,但 $a_i$ 的總和增加 $1$ 了,所以也會是現在的輸入的解。 2. 若輸入為 ``f(K) K`` 的解不存在任何一個 $a_i = 1$,那麼就無解,因為要讓題目要求的第三個條件不變的情況,並要把 $a_i$ 的總和變大,增加的量不可能只有 $1$。 4. 當 $M \ge f(K)$,且 $M - f(K)$ 是 $\ge 3$ 的奇數時,首先先把輸入為 ``f(K) K`` 的解構造出來,然後發現 $a$ 再增加 $1, 2$ 這兩個數字後,題目要求的第三個條件的 xor 結果也不會改變,但 $a_i$ 的數字和增加了 $3$,接著,若要再讓 $a_i$ 的總和增加某個 $d$($d$ 是偶數),就只要把數列 $a$ 的再加入 $2$ 個 $d/2$ 即可。 #### 參考程式碼 ```cpp= // 16 min #include <iostream> #include <vector> #define SZ(X) (int)(X).size() using namespace std; void solve() { int M, K; cin >> M >> K; vector<int> a; int sum = 0; for (int i = 0; i < 5; i++) { if ((K >> i) & 1) { a.push_back((1 << (1 << i)) - 1); sum += a.back(); } } if (sum > M) { cout << "-1\n"; return; } if ((M - sum) % 2 == 0) { a.push_back((M - sum) / 2); a.push_back((M - sum) / 2); } else { if (M - sum == 1) { if (a[0] == 1) a[0]++; else { cout << "-1\n"; return; } } else { int d = M - sum - 3; a.push_back(2); a.push_back(1); a.push_back(d / 2); a.push_back(d / 2); } } cout << SZ(a) << '\n'; for (int i = 0; i < SZ(a); i++) { cout << a[i] << (i + 1 < SZ(a) ? ' ' : '\n'); } } int main() { cin.tie(0); ios_base::sync_with_stdio(0); int t; cin >> t; while (t--) solve(); } ``` ### [Problem 2: Compatible Pair](https://usaco.org/index.php?page=viewproblem&cpid=1507) #### 題目敘述 有非常多隻牛,每個牛都有一個數字代表他的 ID,但有可能有一些牛共用同個 ID。給你正整數 $N$,代表共有 $N$ 種不同的 ID,第 $i$ 種 ID 對應的數字是 $d_i$,並且有 $n_i$ 隻牛的 ID 是 $d_i$ ($1 \le n_i \le 10^9, 0 \le d_i \le 10^9$)。 給你兩個數字 $A, B$ ($0 \le A \le B \le 2 \times 10^9$),兩隻牛若他們的 ID 加起來為 $A$ 或 $B$ 的話,這兩隻牛就可以湊成一對,每隻牛至多只能和其他一隻牛湊成一對,請問所有牛至多可湊成幾對? #### 參考做法 > 此題的參考做法用到了 AA 競程 Level 1「簡易貪心」和「關聯容器」單元裡的知識點 可把每個 ID 都視為圖論上的點,若兩個相異的 ID 可以配對就連一條邊,若一個 ID 自己和自己能配對,就把該點視為「特殊的點」。可發現此圖的每個點度數至多只有 $2$,且不會有環,也就是說此圖是由很多條鏈(整個連通塊本身就是一條路徑(path) 的子圖)構成。 每條鏈之間是互相不影響的,所以可分開考慮,算出各條鏈的答案再加總起來即可。 對於一條鏈,至少會有一個度數為 $1$ 的端點不是「特殊的點」,可發現優先把此點對應的 ID 的牛和他們唯一能配對 ID 的牛配對一定不會比較差,配完後,就可把此點移除,接下來一直做一樣的事,直到最後只剩下特殊的點,再把特殊的點對答案的貢獻算上即可。 存在各式各樣的實作方式,範例程式碼用 dfs 的概念實作。 #### 參考程式碼 ```cpp= #include <algorithm> #include <iostream> #include <map> #include <set> #include <vector> #define SZ(X) (int)(X).size() using namespace std; map<int, int> cnt; // cnt[x] 紀錄 ID 為 x 的牛還有幾隻尚未被配對到 map<int, int> vis; // 紀錄一個點是否已被拜訪過 set<int> special_p; // speicial_p 紀錄那些可以和相同 ID 的牛配對的 ID map<int, vector<int>> e; // 若 ID 為 x 的牛可以和 ID 為 y 的牛配對,那麼 e[x] 裡會含有 y long long ans; void dfs(int x) { for (int y : e[x]) { if (vis[y]) continue; vis[y] = 1; int mi = min(cnt[x], cnt[y]); cnt[x] -= mi; cnt[y] -= mi; ans += mi; dfs(y); } } int main() { cin.tie(0); ios_base::sync_with_stdio(0); int N, A, B; cin >> N >> A >> B; for (int i = 0; i < N; i++) { int n, d; cin >> n >> d; cnt[d] = n; } for (auto [d, n] : cnt) { for (int x : {A - d, B - d}) { if (d == x) special_p.insert(d); else { auto it = cnt.find(x); if (it != cnt.end()) { e[d].push_back(x); } } } } // 若 A = B, 需把重複刪掉 sort(e[d].begin(), e[d].end()); e[d].erase(unique(e[d].begin(), e[d].end()), e[d].end()); for (auto &[x, vi] : e) { // 若發現一個點度數是 1 且尚未被拜訪過,且不是「特殊的點」,就從它開始 dfs // 每次枚舉到一條邊,就把該邊對應到的兩個 ID 的牛能配多少就配多少 if (SZ(vi) == 1 && !vis[x] && special_p.find(x) == special_p.end()) { dfs(x); } } // 最後還要考慮相同 ID 能配的牛 for (int d : special_p) { ans += cnt[d] / 2; } cout << ans << '\n'; } ``` ### [Problem 3: Ski Slope](https://usaco.org/index.php?page=viewproblem&cpid=1508) #### 題目敘述 Bessie 與她的朋友們準備去滑雪旅行。這座山上有 $N$ 個「中繼點」(waypoints),分別標記為 $1, 2, \ldots, N$,海拔依照編號遞增(其中中繼點 $1$ 在山底)。 對於每個中繼點 $i$,都有一條滑雪道從 $i$ 處開始,一直延伸到 $p_i$ ($1 \le p_i < i$) 處。每條滑雪道各自有一個「難度」(difficulty) $d_i$ 與「樂趣」(enjoyment) $e_i$,其中 - $0 \le d_i \le 10^9$ - $0 \le e_i \le 10^9$ Bessie 有 $M$ 位朋友($1 \le M \le 10^5$),他們將依照以下方式滑雪: - 每位朋友選擇一個起始中繼點 $i$; - 從該中繼點開始,沿著滑雪道依序往下滑($i \to p_i \to p_{p_i} \to \dots$),最終抵達中繼點 $1$。 對於滑過的每一條滑雪道,朋友所獲得的「樂趣」會累加;整段旅程的總樂趣即為沿途所有滑雪道的樂趣總和。 不過,每位朋友也有自己不同的「技能值」(skill) $s_j$ 與「勇氣值」(courage) $c_j$,範圍為: - $0 \le s_j \le 10^9$ - $0 \le c_j \le 10$ 第 $j$ 位朋友只能選擇路徑上難度超過 $s_j$ 的滑雪道至多只有 $c_j$ 條的路徑。 你的任務是:對於每位朋友,計算他能獲得的最大樂趣總和。 --- #### 輸入格式 1. 第一行輸入單一整數 $N$($1 \le N \le 10^5$),表示中繼點的數量。 2. 接下來從第 2 行到第 $N$ 行(共 $N-1$ 行),每行包含三個以空格分隔的整數: - $p_i$:第 $i$ 個中繼點有滑雪道通往中繼點 $p_i$ - $d_i$:第 $i$ 個中繼點的滑雪道的「樂趣」 - $e_i$:第 $i$ 個中繼點的滑雪道的「難度」 3. 接著讀入一行,包含單一整數 $M$($1 \le M \le 10^5$),表示 Bessie 的朋友人數。 4. 之後的 $M$ 行中,每行包含兩個以空格分隔的整數: - $s_i$:第 $i$ 位朋友的技能值 - $c_i$:第 $i$ 位朋友的勇氣值 #### 其他說明 - 由於本題中可能出現非常大的整數運算,建議在程式實作時使用 64 位整數型態(例如 C/C++ 中的 `long long`)以避免溢位。 #### 參考做法 > 此題的參考做法用到了 AA 競程 Level 2「深度優先搜索(DFS)」和 Level 1「關聯容器」單元裡的知識點,並假設大家已有 DFS 的 backtracking(回溯法) 的概念 首先注意到一個重點: 若第 $j$ 位朋友可以從中繼點 $i$ 出發,代表著:「該條路徑難度第 $c_j+1$ 大(從 $1$ 開始編號) 的滑雪道值小於等於 $s_i$。」 於是若不管時間複雜度,我們可以有個 $O(N^2M)$ 的做法如下: > 每個朋友的答案各自計算,再枚舉所有中繼點 i 作為起點的情況,搜集該路徑所有滑雪道難度,求出第 c_i + 1 大的難度值,若該值 <= s_i,那麼此路徑的「樂趣」總和就能更新此位朋友的答案 接著我們來一步一步優化此做法。 **1. 利用DFS+回溯的概念優化** 上述比較暴力的做法是每個朋友都要獨立計算一次,但每次計算都要重新算一次每條路徑第 $c_j+1$ 大的困難度,況且 $c_j$ 的值其實只有 $11$ 種,若可以一剛開始就算出來,那對時間複雜度優化肯定是有幫助的。 這部分我們可使用 DFS+回溯的概念,改成從 $1$ 號中繼點當起點 DFS,並在全域維護一個 multiset,儲存 DFS 時起點到當前點路徑上所有滑雪道的難度,這樣 DFS 時每走到一個中繼點 $i$,就能求出 $1$ 好中繼點到 $i$ 號中繼點的路徑中前 $11$ 大困難度的值了。這部分總時間複雜度只要 $O(N log N)$ (此時間複雜度的計算是把 $11$ 當常數) **2. 把資料整理成單調序列,以便利用 upper_bound 優化 接著,對於相同 $c_j$ 值,我們回溯後可以得到許多個 pair $(x, y)$,代表若第 $j$ 位朋友的技能值($s_j$) 大於等於 $x$,就可以有總和為 $y$ 的「樂趣」,若存在兩個 pair $(x1,y1)$ 和 $(x2, y2)$,若 $x2 \ge x1$ 但 $y2 < y1$ ,那麼 $(x2, y2)$ 至個 pair 就不需要考慮了,因為他不會比 $(x1, y1)$ 還好。於是我們可先把所有 pair 按照 $x$ 值排序,排完後,再把剛才說不需要考慮的那些 pair 移除,剩下的 pair 就會隨著 $x$ 的遞增,$y$ 也是遞增的。於是對於每個詢問,我們就可以使用 upper_bound 找到 $\le s_i$ 的最大的 $x$ 的 pair,此 pair 的 $y$ 值就是該詢問的答案。 #### 參考程式碼 ```cpp= #include <algorithm> #include <array> #include <iostream> #include <set> #include <vector> #define SZ(X) (int)(X).size() using namespace std; using ll = long long; const int SIZE = 1 << 17; struct Edge { int y, difficulty, enjoyment; }; vector<Edge> e[SIZE]; multiset<int> difs; vector<array<ll, 2>> pp[11]; void dfs(int x, ll len) { auto it = difs.rbegin(); for (int i = 0; i <= 10; i++) { if (it != difs.rend()) { pp[i].push_back({*it, len}); it++; } else { pp[i].push_back({0, len}); } } for (int i = 0; i < SZ(e[x]); i++) { difs.insert(e[x][i].difficulty); dfs(e[x][i].y, len + e[x][i].enjoyment); difs.erase(difs.find(e[x][i].difficulty)); } } int main() { cin.tie(0); ios_base::sync_with_stdio(0); int N; cin >> N; for (int i = 2; i <= N; i++) { int p, d, en; cin >> p >> d >> en; e[p].push_back({i, d, en}); } dfs(1, 0); for (int i = 0; i <= 10; i++) { sort(pp[i].begin(), pp[i].end()); ll last_v = -1; int it = 0; vector<array<ll, 2>> tmp; for (int j = 0; j < SZ(pp[i]); j++) { if (pp[i][j][1] <= last_v) continue; last_v = pp[i][j][1]; tmp.push_back(pp[i][j]); } pp[i].swap(tmp); } int M; cin >> M; while (M--) { int s, c; cin >> s >> c; auto pos = upper_bound(pp[c].begin(), pp[c].end(), array<ll, 2>{s + 1LL, -1LL}) - pp[c].begin(); pos--; if (pos < 0) cout << "0\n"; else { cout << pp[c][pos][1] << '\n'; } } } ``` ## Gold ### [Problem 1: Moo Decomposition](https://usaco.org/index.php?page=viewproblem&cpid=1509) #### 簡要題意 輸入給你三個正整數 $K, N, L$ ($1 \le N, K \le 10^6, 1\le L \le 10^{18}$),以及給你一個長度為 $N$ 且由英文大寫字母 `'M'` 和 `'O'` 組成字串 $s$,而你要處理的真正字串是由 $s$ 複製 $L$ 次接起來的字串。例如說,當 $s = $ `"MOMOOO"`,$L = 3$,那麼你要處理的真正字串是:`"MOMOOOMOMOOOMOMOOO"`。 定義 $MOO$ 字串是由一個 `'M'` 後面接上 $K$ 個 `'O'` 形成的字串。 現在請你回答以下問題: 要把 $s$ 複製 $L$ 次接起來的字串拆成很多個子序列,使得每個子序列都是長度為 $MOO$ 字串,請問有幾種拆法?(保證至少有 $1$ 種拆法) 請輸出答案除以 $10^9+7$ 的餘數。 例如,當 $K=2,N=4,L=1$ 且 $s=$ `"MOMOOO"`,就有 $3$ 種拆法: 1. 第一個子序列位置是 $1,2,4$,第二個子序列位置是 $3,5,6$ 2. 第一個子序列位置是 $1,2,5$,第二個子序列位置是 $3,4,6$ 3. 第一個子序列位置是 $1,2,6$,第二個子序列位置是 $3,4,5$ #### 參考做法 > 此題的參考做法用到了 AA 競程 Level 4「特殊 DP 題型」裡所介紹的組合數相關知識。 看完這道題我第一個想法是:「這道題放在金組也太簡單了吧...」 首先,由於題目保證至少有一種拆法,所以字串 $s$ 裡 `'O'` 的數量一定是 `'M'` 的 $K$ 倍,接著 $L$ 這個參數對此題難度的影響非常少,因為可發線每個子序列一定是在同一份複製出來的 $s$ 裡,所以 $s$ 複製 $L$ 次的答案其實就是只有一個 $s$ 的答案的 $L$ 次方。 最後來解說要怎麼計算當 $L = 1$ 時的答案。 我們只要從右往左枚舉字串裡的每個字元,並用一個變數 $cnt$ 紀錄目前有多少個被枚舉到的字母 `'O'` 還沒被確定是放在哪個子序列裡,而當我們枚舉到 `'O'` 時 $cnt$ 就增加 $1$;而當枚舉到 `'M'` 時,我們就決定這個 `'M'` 後面要接哪些字母 `'O'`,因為還有 $cnt$ 個字母 `'O'` 還沒決定要放在哪個子序列,所以我們就有 $cnt \choose K$ 種選擇方式,可直接把此值乘上答案後,在把 $cnt$ 的值減少 $K$ 即可。時間複雜度為 $O(N)$。 #### 參考程式碼 ```cpp= #include <algorithm> #include <iostream> #include <vector> #define SZ(X) (int)(X).size() using namespace std; const int MOD = (int)1e9 + 7; const int SIZE = 1'000'001; long long mypow(long long x, long long y, int mod = MOD) { x %= mod; long long res = 1 % mod; while (y) { if (y & 1) res = res * x % mod; y >>= 1; x = x * x % mod; } return res; } long long fac[SIZE]; void pre() { fac[0] = 1; for (int i = 1; i < SIZE; i++) fac[i] = fac[i - 1] * i % MOD; } long long C(long long x, long long y) { if (y < 0 || y > x) return 0; return fac[x] * mypow(fac[y] * fac[x - y], MOD - 2) % MOD; } int main() { cin.tie(0); ios_base::sync_with_stdio(0); pre(); int K, N; long long L; cin >> K >> N >> L; string s; cin >> s; long long ans = 1; int cnt = 0; for (int i = SZ(s) - 1; i >= 0; i--) { if (s[i] == 'O') cnt++; else { ans = ans * C(cnt, K) % MOD; cnt -= K; } } cout << mypow(ans, L) << '\n'; } ``` ### [Problem 2: Election Queries](https://usaco.org/index.php?page=viewproblem&cpid=1510) #### 簡要題意 給你一個長度為 $N$ 的數列,$a_1, a_2, \ldots, a_N$,接下來有 $Q$ 次詢問,每次詢問都會給你兩個正整數 $i, x$ 代表你要把 $a_i$ 的值改為 $x$,請你回答以下問題: 請把數列 $a$ 拆成兩個非空的子序列,使得此兩子序列的眾數的差的絕對值盡可能大(如果眾數有很多個,可以任取一個)。舉例來說,數列 $1, 4, 1, 5, 4, 4, 4$ #### 參考作法 > 此題的參考做法用到了 AA 競程 Level 5「根號資料結構與演算法」單元的知識點 先提一下,我覺得出題者使用的方法極可能和我的方法不一樣,這種複雜的資料結構題通常都有好多種做法。 --- 定義 $cnt[x]$ 代表數字 $x$ 在 $a$ 裡出現幾次。 先介紹 4 個觀察: 1. 若數字 $x$ 和數字 $y$ 可以成為兩個子序列的眾數,那麼 $cnt[x] + cnt[y] \ge \max\limits_{i=1\sim N}{cnt[i]}$ 因為你只要把全部的 $x$ 放在第一個子序列,全部的 $y$ 放在第二個子序列,然後剩下的每個數字就好好的分配到兩個子序列,使得在第一個子序列的數量不要超過 $cnt[x]$,在第二個子序列的數量不要 $cnt[y]$ 即可。 2. $cnt[x]$ 的不同的值的數量為 $O(\sqrt{n})$,這是老梗就不解釋了。 3. 定義集合 $S_i = \{x | cnt[x]=i\}$,對於每個 $S_i$,利用貪心的概念,在計算答案時,都只要考慮 $S_i$ 中最大和最小的數字即可,綜合第 2 個觀察,每個詢問我們都只要考慮 $O(\sqrt{n})$ 個數字。 4. 如果兩個子序列裡的眾數只需要從其中 $O(\sqrt{n})$ 個數去考慮,且這些數已經根據 $cnt[x]$ 的值排序好的話,那我們很容易用雙指標一類的方法 $O(\sqrt{n})$ 算出答案。 有了第 $4$ 個觀察後,如果我們有辦法每個詢問都只花 $O(\sqrt{n})$ 的時間把這 $O(\sqrt{n})$ 個要考慮的數字找出來,那麼此題就可以用 $O(Q \sqrt{N})$ 的時間解決啦! 而要找到這些要考慮的數字,其實只要找到所有非空的 $S_i$ 的索引值即可。 至於這件事我們要用根號分塊來解決。 在程式碼裡,我們會真的使用 STL 的 set 表示觀察 3 裡的 $S_i$(程式碼裡叫做 $ids[i]$),可以發現,每個詢問裡只會改變兩個數字的數量,也就是至多會變動到 $4$ 個 $S_i$。 有了這樣的觀察後其實就有很多種解決方式了,這裡只介紹我程式碼使用的方法: 把集合 $S_i$ 每 $GROUP\_SIZE$ 個一組,並用集合 $exist\_number[j]$ 儲存第 $j$ 組中非公的 $S_i$ 的索引值。 每個詢問都變動到的 $S_i$ 至多只會出現在 $4$ 組裡,再用 $O(GROUP\_SIZE)$ 的時間暴力更新這幾組的 $exist\_number[i]$,更新完後,在用 $O(N/GROUP\_SIZE+\sqrt{N})$ 的時間把所有集合 $exist\_number[i]$ 裡的索引值按照小到大搜集起來即可。 細節請直接參考程式碼。 #### 參考程式碼 ```cpp= #include <algorithm> #include <iostream> #include <set> #include <vector> #define SZ(X) (int)(X).size() using namespace std; const int SIZE = 200'001; const int GROUP_SIZE = 512; int N; int cnt[SIZE]; set<int> ids[SIZE]; // ids[i] 紀錄所有出現在 a 裡數量為 i 的數字 // exist_number[j][*] 紀錄 S 集合序列中第 $j$ 組裡非空集合的編號們 // exist_num[j] 是第 $j$ 組裡非空的集合數量 int exist_number[GROUP_SIZE][GROUP_SIZE]; int exist_num[GROUP_SIZE]; void add(int x) { int id = x / GROUP_SIZE; exist_number[id][exist_num[id]++] = x; } // 重新計算 $S$ 集合序列中第 seg_id 組有哪些非空集合 void recalc(int seg_id) { exist_num[seg_id] = 0; for (int i = seg_id * GROUP_SIZE; i < seg_id * GROUP_SIZE + GROUP_SIZE && i <= N; i++) { if (SZ(ids[i])) add(i); } } int hid[SIZE], a[SIZE]; int main() { cin.tie(0); ios_base::sync_with_stdio(0); int Q; cin >> N >> Q; for (int i = 1; i <= N; i++) { cin >> a[i]; cnt[a[i]]++; } for (int i = 1; i <= N; i++) { if (cnt[i]) ids[cnt[i]].insert(i); } for (int i = 1; i <= N; i++) { if (SZ(ids[i])) { add(i); } } while (Q--) { int i, x; cin >> i >> x; if (a[i] != x) { set<int> tmp; ids[cnt[a[i]]].erase(a[i]); if (ids[cnt[a[i]]].empty()) tmp.insert(cnt[a[i]] / GROUP_SIZE); cnt[a[i]]--; if (cnt[a[i]]) { if (ids[cnt[a[i]]].empty()) tmp.insert(cnt[a[i]] / GROUP_SIZE); ids[cnt[a[i]]].insert(a[i]); } if (cnt[x]) { ids[cnt[x]].erase(x); if (ids[cnt[x]].empty()) tmp.insert(cnt[x] / GROUP_SIZE); } cnt[x]++; if (ids[cnt[x]].empty()) { tmp.insert(cnt[x] / GROUP_SIZE); } ids[cnt[x]].insert(x); a[i] = x; for (int seg_id : tmp) { recalc(seg_id); } } // 以下這段程式碼會把所有非空的 S_i 的 i 值搜集起來放在 hid 陣列裡 int hn = 0; for (int i = 0; i * GROUP_SIZE <= N; i++) { for (int j = 0; j < exist_num[i]; j++) { hid[hn++] = exist_number[i][j]; } } int bb = hid[hn - 1]; int j = hn - 1; int mi = 1e9; int ma = -1e9; int ans = 0; // 用雙指標計算答案 for (int i = 0; i < hn; i++) { while (j >= 0 && hid[j] + hid[i] >= bb) { ma = max(ma, *ids[hid[j]].rbegin()); mi = min(mi, *ids[hid[j]].begin()); j--; } ans = max(ans, ma - *ids[hid[i]].begin()); ans = max(ans, *ids[hid[i]].rbegin() - mi); } cout << ans << '\n'; } } ``` ### [Problem 3: OohMoo Milk](https://usaco.org/index.php?page=viewproblem&cpid=1511) #### 簡要題意 有一個長度為 $N$ 的數列 $m_1, m_2, \ldots, m_N$ ($1 \le m_i \le 10^9$),Bessie 和 Elsie 根據此數列完以下遊戲: 1. 遊戲共有三個參數 $D, A, B$ ($1 \le A < B \le N, 1 \le D \le 10^9$)。 1. 遊戲共會進行 $D$ 輪。 2. 每一輪開始時,首先 Bessie 會把數列 $m$ 的其中 $A$ 個數增加 $1$,接著 Elsie 會把其中 $B$ 個大於 $0$ 的數減 $1$。 3. 遊戲最終分數的計算方式為 $\sum\limits_{i=1}^N m_i$,Bessie 的目標是讓分數盡可能高,而 Elsie 的目標是讓分數盡可能小 假設 Bessie 和 Elsie 都採取最佳策略,請問遊戲最終分數會是多少? #### 參考作法 > 此題的參考做法用到了 AA 競程 Level 2「二分搜常見題型」單元的知識點 首先,可用貪心的思維證明在最佳策略中, Bessie 一定是選 $m$ 中最大的 $A$ 個數增加 $1$,而 Elsie 也會是選 $m$ 中最大的 $B$ 個數減 $1$,就結論而言,每一輪操作會是數列 $m$ 中第 $B+1$ 大的數至第 $A$ 大的數都增加 $1$。 那麼除了最大的 $A$ 個數以外的數肯定是維持不變的,就先把他們忽略,最終答案再加上這些數的平方和,所以我們就直接當作數列 $m$ 長度為 $A$,每次操作則是會把數列裡最小的 $A-B$ 個數增加 $1$。 但這題困難的點在於回合數實在太多了,那該怎麼辦呢? **(以下已經把數列 m 的長度視為 A 了)** 模擬一些 case 後可發現,由於我們一定是盡可能讓比較小的數變大,最終數列 $m$ 的長相一定是存在某個數字 $x$,原本數列 $m$ 裡小於等於 $x$ 的 $m[i]$ 都會變為 $\min(m[i] + D, x)$,或是變為 $x+1$ (一定變為這樣的證明請參考 [CF1551 B2. Wonderful Coloring - 2](https://codeforces.com/problemset/problem/1551/B2) 的題解,其實和這題 Codeforces 題是樣的東西),那我們就去二分搜這個 $x$ 值即可,也就是說,要找到最大的 $x$,使得 $\sum_{i=1}^A max(0, min(x-m[i], D)) \le D \cdot (A-B)$,二分搜求出 $x$ 值之後,就不難得到數列 $m$ 的最終長相,然後就可以算出最終得分。 #### 參考程式碼 ```cpp= #include <algorithm> #include <iostream> #include <vector> #define SZ(X) (int)(X).size() using namespace std; const int MOD = (int)1e9 + 7; template <typename T> void add(T &x, long long v) { x = (x + v) % MOD; if (x < 0) x += MOD; } void solve() { int N, D; cin >> N >> D; int A, B; cin >> A >> B; vector<int> m(N); for(int &x: m) cin >> x; sort(m.begin(), m.end(), greater<>()); long long low = m[A - 1], hi = m[0] + D; while (low <= hi) { long long mid = (low + hi) / 2; long long need = 0; for (int i = 0; i < A; i++) { need += max(0LL, min(1LL * D, mid - m[i])); } if (need < 1LL * D * (A - B)) low = mid + 1; else hi = mid - 1; } int h = low - 1; long long use = 0; long long ans = 0; for (int i = 0; i < A; i++) { if (m[i] < h) { int inc = min(h - m[i], D); use += inc; m[i] += inc; } } for (int i = 0; i < N; i++) { if (m[i] == h && use < 1LL * (A - B) * D) { use++; m[i]++; } add(ans, 1LL * m[i] * m[i]); } cout << ans << '\n'; } int main() { cin.tie(0); ios_base::sync_with_stdio(0); solve(); } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully