比賽補題紀錄

ICPC - Universal Cup

Season 3

Contest ABCDEFGHIJKLM
Stage 3: Ukraine A B C D E F G H I J K! L
Stage 8: Cangqian A B C D E F G H I J! K L M
Stage 14: Harbin A B C D E F G H I J K L M
Stage 16: Nanjing A B C D E F G H I J K L M
Stage 21: Ōokayama A B C D E F G H I J K L M
Stage 24: Poland A B C D E F G H I J K L
Stage 25: Hangzhou A B C D E F G H I J K L M
Stage 28: Haidian Huangzhuang A B C D E F G H I J K

ICPC - Africa and Arab


ICPC - Asia East Continent

Final

Contest ABCDEFGHIJKLM
2021-2022 Final A B C D E F G H I J K L M

ICPC - Asia Pacific

Championship

Contest ABCDEFGHIJKLM
2024-2025 Championship A B C D E F G H I J K L M

Indonesia

Contest ABCDEFGHIJKLM
2020-2021 Jakarta A B C D E F G H I J K L M
2024-2025 Jakarta A B C D E F G H I J K L M

Japan

Contest ABCDEFGHIJKL
2024-2025 Yokohama A B C D E F G H I J K L

South Korea

Contest ABCDEFGHIJKL
2023-2024 Seoul A B C D E F G H I J K L
2024-2025 Seoul A B C D E F G H I J K L

Taiwan

Contest ABCDEFGHIJKLMN
2024-2025 Taichung A B C D E F G H I J K L M N

Vietnam

Contest ABCDEFGHIJKLM

ICPC - Asia West Continent

Contest ABCDEFGHI
2023-2024 Final A B C D E F G H I

ICPC - Europe

Contest ABCDEFGHIJK
2024-2025 Championship A B C D E F G H I J K

ICPC - Latin America

Championship

Contest ABCDEFGHIJKL
2024-2025 Championship A B C D E F G H I J K L

ICPC - North America


ICPC - Northen Eurasia


ICPC - South Pacific


ICPC - Other

Contest ABCDEFGHIJKLMN
2024-2025 CTU Open Contest A B C D E F G H I J K L
2024-2025 ICPC, NERC, Southern and Volga Russian Regional Contest A B C D E F G H I J K L M N

註釋

Color Meaning
Green 已經 AC 並寫完題解
Green ! 已經 AC 並寫完題解,但有更好的做法待補
Blue 已經 AC / 精神 AC,且未打算寫題解
Yellow 已經 AC,但未寫完題解
Red 呃,我不知道(寫了題解,但是是假解)
White 尚未(正常的)AC
  1. 不一定會每題(每個 subtask)都寫題解,有些太枯燥的或沒有啟發性的會被跳過。
  2. 有酷炫實作方法的可能會被特別提到,否則像一些 casework DP 就自己看 submission 就好。

我的題目

因為出了好多題目好像都沒有寫好題解,決定來整理一下,但優先級不高。

  • 0x00 ~ 0x08 待補
  • IONCamp 2022 ~ 2024 待搬運
  • 全國模擬賽 2021 ~ 2023 待搬運
  • YTP 2023 ~ 2024 待搬運
  • 演算法概論 待補
  • 還有許多......
  • 目前總共應該是 120 題,不知道要補多久......

The 3rd Universal Cup. Stage 3: Ukraine

第一篇做題筆記,不以時間軸的方式記錄比賽,只記錄解題過程。

這場難度在 UCup 中算很低,最強的 USA1 甚至在兩個小時以內就破台了。

Problem A. Aibohphobia

Tags: casework, palindrome

給定字串 ,請將其重新排列使每個長度 的前綴都不是回文。

  • 多筆測資、

顯然只有一種字元無解,當有兩種字元的時候要 ,可以得到必須要有某個字元只出現一次,否則無解。

當有三種以上的字元時,將字串排序並 reverse(1 + ALL(s)) 後輸出即可。

Problem B. Breaking Bad

待補

給定一個 的矩陣 ,請問在所有 的排列 中, 可能有哪些值。

Problem C. Chemistry Class

Tags: greedy

給定 個整數 ,請將其兩兩配對滿足

  • 不存在差距 的配對(不合法
  • 差距 的配對數量盡量多(好配對
  • 多筆測資、、值域

以下令差距在 之間的配對為 壞配對

以下令 ,觀察:

  • 當配對 合法,則改成 也合法。
  • 當配對 都是好配對,則改成 也是好配對。
  • 當配對 都是壞配對,則改成 也仍然合法。

因此,可以得出以下性質:

  1. 配對不會相交,可以看成括弧序列。
  2. 好配對不會包著任意配對,也就是說好配對一定是 的形式。
  3. 壞配對只可能包著好配對,也就是說一定是 BGG...GGB 中間有偶數個 G 的形式。

採用 greedy 配對的方式,由左至右配對 GG,當不會讓壞配對 B...GGB 差距超過 時就配對,否則就配對壞配對。

因為這總是保證了最多的好配對,所以 greedy 可以得到最佳解。

Problem D. Daily Disinfection

Tags: casework

給定長度 字串 ,每次可以交換相鄰兩個字元,求最少交換次數使每個位置都出現過至少一次 0

  • 多筆測資、

每個 1 都一定要移開,假設 0 那每個 1 都一定能往右移動。

觀察到只有在 1.101.101.101.1 這種 case(頭尾都是 1 且沒有連續的 0)才會讓 1 必須被挪動第二次,此時一定是挪動最小塊的連續 1

Problem E. Equalizer Ehrmantraut

Tags: math

請求出有多少個長度為 的整數序列對 滿足 且對所有 皆有

輸出。

長一樣當然都是合法的,考慮當有數字不同的時候,W.L.O.G. 是第一個不同的位置且 非嚴格遞增:

  • 此時 一定等於 ,由 可以推得
  • 此時 一定 ,由 可以推得

也就是合法的序列對長相是 。枚舉 中的最大值 後可以得到答案:

即可 求解。

Problem F. Formal Fring

待補

定義 是非負整數集合 的數量,滿足 且不存在 使得

請求出 的值。

Problem G. Goodman

AC 但還不會證明,待補

定義兩個 的 permutation 的複合

給定一個排列 ,請求出一個排列 使 最大。

  • 多筆測資、

Problem H. Highway Hoax

Tags: dp, ntt

給一棵 個點的樹,邊是有向的,一開始有某些位置各有一個棋子,每次操作可以將一個棋子沿著出邊移到相鄰的點,但移動完後會將邊反轉。

定義合法狀態是每個點的棋子數量皆 ,求從原始盤面可以抵達多少種合法狀態。

輸出。

先觀察到任意邊 只會有至多一次的 操作,移回去再移回來並沒有意義。當對這棵樹定根後,會具有以下性質:

  • 合法狀態下,每個子樹的棋子數量必定是原始棋子數量 以內,取決於跟父親連邊的方向。

這就會想使用 DP 解決:令 代表以 為根的子樹,且 有多一個 / 剛剛好 / 少一個棋子的合法狀態數量。這樣就可以得到以下轉移:

待補

每個小孩的 DP 值對應到多項式

轉移可以把所有小孩的多項式 offset 一個 之後 NTT 乘起來,但要注意要每次挑最短的兩個多項式做 NTT,否則複雜度仍會退化為

一個方便的 trick 是把所有多項式丟進 queue(deque)裡,每次取出前兩個做事之後再放到最後面,這個的複雜度可以透過參考線段樹的長相感性證明。

Problem I. Increasing Income

Tags: dp

定義兩個 的 permutation 的複合

給定一個排列 ,請求出一個排列 使 最大。

  • ,也就是前綴最大值數量。
  • 多筆測資、
  • 首先,一定有下界 跟上界
Proof
  • 下界: 時答案必定
  • 上界:如果答案 ,那一定有至少 個位置在 之中都有貢獻。
    把那些有貢獻的位置抓出來,他們會形成更長的 LIS,產生矛盾。

現在考慮如何構造滿足上界的答案,首先先把 LIS 抓出來( 滿足 )。

接著要處理剩下的元素,先 獨立 觀察 可以放的位置:

  • 在不破壞 的貢獻的前提下插進 裡面:當滿足 時可以將 放到 中。
  • 在讓自身能產生貢獻的前提下插進 裡面:當滿足 時可以將 放到 中。

總上兩點,可以得到當 放在滿足 的最小的 前面就能不破壞原有的貢獻並維持自身的貢獻。

如果有多個 可以放在 之間,這些數字必定可以被分到兩類:。每個數字必定且只滿足其中一個條件(否則就不會被放在這裡),只要確保所有第一類的數字依照位置 排序、第二類的數字依照值 排序,就能保證所有區間內的數字都有貢獻。

時間複雜度:

Problem J. Jesse's Job

Tags: N/A

給定一個排列 ,你要將排列中的元素塗上黃色 Y 或紅色 R(至少各一),並最大化將黃色跟紅色分別排序之後的

請構造一組解。

  • 多筆測資、

先建 的有向邊,此時圖會被分成許多個環。觀察到如果圖有兩個以上的連通塊,對各自分別排序就能將整個陣列排序。以下考慮圖只有一個連通塊的情況。

如果把環切成 兩個部分:,滿足 ,則可以發現:

  • 選了位置 會選到數字 (選了位置 會選到數字 )。
  • 選了位置 會選到數字 (選了位置 會選到數字 )。

因為 比所有其他數字都還要小,所以把位置 排序後只會讓 ;把位置 排序後只會讓

至此,就得到了排好 個數字的方法,而顯然在只有一個環的情況下不存在排好恰 個數字的方法。

Problem K. Knocker

Tags: dp

給定正整數陣列 ,你可以選擇若干數字 並令 ,請問有幾種可能的

輸出。

  • 每次的 操作必定可以取
Proof

此處我們嘗試證明當 太小可以換成多個更大的 達成相同操作。

假設最大值 ,那麼取 也可以讓所有會被修改到的數字變成跟 一樣,此後最大值必定

接著取 ,一直到 ,每次的操作只會把 的數字變成

注意到每次操作中剩下的數字最大是 ,而 顯然為真。

上述 Claim 1 的最大用處是把取 操作變成減法操作,注意對一個數字「減法」之後他會變成不到一半。

  • 對每個合法的狀態都存在一種每個數字只被「減法」影響到至多一次的方案。
Proof

先將陣列由大到小排序過,可以發現一個「減法」操作影響到的是一段前綴,而且這段前綴在被「減法」之後仍然遞減。

當有另一個減法操作()影響到該區間時,該區間會變成「被 影響到的前綴」跟「只被 操作影響到的後綴」。那不如將創造出這段區間的那次操作()先變成 做完前綴,再做一次 操作解決後綴。

注意到因為前綴數字 by Claim 1,所以並不會被 都影響到。

將陣列降序排序後,就可以來嘗試 DP 了:狀態 代表 是第一個沒有被「減法」的數字,且當前(為了不二次影響前面的數字)能取的 下界是 的陣列數量。

轉移採用遞推,枚舉合法的 作為轉移,維護會被影響到的數字( 的最小 ),再拿滿足轉移條件的狀態 去更新

時間複雜度:

轉移應該可以用前綴和壓掉一個 ,待補。

Problem L. Lalo's Lawyer Lost

待補

給定一棵 個點的仙人掌,定義 是兩點的最短距離,請將這些點分成 個 pair,使得 最大。

  • 多筆測資、

The 3rd Universal Cup. Stage 8: Cangqian

  • 連結:https://qoj.ac/contest/1780
  • 時間:2025 Apr 2, 07:00-12:00
  • 團隊:SorahISA
  • 成績:5 / 13, Penalty 596, dirt 44% ( A B C D E F G H I J K L M )

在 A 大坐牢,寫了 python 不知道為什麼會 RE 卡了一個小時。

Problem A. Bingo

待補

Problem B. Simulated Universe

待補

Problem C. Challenge NPC

Tags: constructive

構造一張簡單無向圖 使得存在一個正整數 滿足最小點著色數 且 greedy 解至少要用 個顏色。

  • Greedy 解:對 ,將點 的顏色(此處的 -based)。

這麼大,感覺就可以弄一張二分圖做到 ,所以就做完了。

只要把第 小的奇數/偶數連向第 小的偶數/奇數就可以了。

  • 因為是二分圖,所以最佳解顯然是
  • 小的奇數/偶數會被 greedy 解填上顏色

Problem D. Puzzle: Easy as Scrabble

待補

Problem E. Team Arrangement

Tags: greedy, bit operation

要把 個人分成若干組,第 個人希望自己在的小組有 個人。如果一種分組方法每一組分別有 個人,那麼總價值就是

請求出最大價值的分組,或判斷其不可能達成。

第一直覺是先枚舉每一組的大小,再判斷該分組能不能被達成。 個人的分組數量是 ,而 ,於是大概要一個 的方法來判斷。

最簡單的方法是 greedy:先將區間們依照右界小到大排序,再每次取 的 lower bound 出來。用 multiset 果不其然的吃了 TLE,改成 map 之後有稍微改善,但複雜度仍然有一個

注意到這題 ,每一組的大小也只有 以下,可以用一個 uint64_t 來儲存!

  • 跟用 map 的做法一樣,存在 (val >> x) & 1
  • 的 lower bound 可以用 __builtin_ctzll(val >> l << l) 來做,這樣就可以在 的時間內找到。
    • 注意 __builtin_ctzll(0) 會是 undefined behavior,可以特判,或是一開始就放 1 << (n+1)val 裡面。

於是這樣的複雜度就變成 了。

Problem F. Stage: Agausscrab

水題,跳過

Problem G. Crawling on a Tree

待補

Problem H. Permutation

待補

Problem I. Piggy Sort

待補

Problem J. Even or Odd Spanning Tree

Tags: mst, lca

給一張帶正邊權的 邊無向圖 ,求 weight 分別是奇數跟偶數的最小生成樹,輸出 weight 即可(不存在則輸出 )。

  • 多筆測資、

顯然有一邊的答案就是 MST 本身。

  • 是選了 條奇邊的最小生成樹 weight,如果最小生成樹選了 條奇邊,那答案一定是
Proof

根據經典 Aliens 題「黑白生成樹」可以證明 有凸性。

  • 要得到 只要枚舉樹外邊 路徑上奇偶性相異的最大值換就好。
非常不嚴謹的 Proof

隨意抓一棵最小生成樹 ,其權重是

考慮原本 路徑上的一條邊 被拔掉,替換成 ,從而使 這條邊成為 路徑的新最大值。把 邊加進來之後的 會變成一棵基環樹,環上包含 三條邊跟其他一些邊。

這樣做會使答案變好的條件是 ,但這種情況下 會是權重更小的 MST,與原假設不符。

應該可以推廣到替換任意條邊的情況,但我推不出來。

順帶一提,官解用「這是擬陣的性質」一筆帶過,但我不熟擬陣不會證。有機會再補證明。

於是只要用 LCA 維護路徑上的奇數 weight、偶數 weight 最大值是多少就可以了。

時間複雜度:

Problem K. Sugar Sweet 3

待補

Problem L. Challenge Matrix Multiplicatio

待補

Problem M. Triangles

待補

The 3rd Universal Cup. Stage 14: Harbin

  • 連結:https://qoj.ac/contest/1817
  • 時間:2024 Oct 27, 02:00-07:00
  • 團隊:ネクロマンサー (SorahISA)
  • 成績:6 / 13, Penalty 774, dirt 0% ( A B C D E F G H I J K L M )

沒看計分板以為 I 是水題就 AC 了,結果看了記分板之後才發現是測資太爛 + 自己不會估複雜度!?

A. Build a Computer

Tags:

請構造一個至多 個點的自動機,該自動機需要且只能 accept 間數字沒有前導零的二進位表示法。

  • 舉例來說,當 時,自動機需要且只能接受 這三個字串。
  • ,你構造的圖只能有一個起點和一個終點,且不能有環

待補

B. Concave Hull

Tags: convex hull

給定 個點,求其所有不是 convex 的 simple polygon 的最大面積

  • 多筆測資、、沒有三點共線

待補

C. Giving Directions in Harbin

水題,跳過

D. A Simple String Problem

Tags:

題目

  • 測資

待補

E. Marble Race

Tags:

題目

  • 測資

待補

F. 1D Galaxy

Tags:

題目

  • 測資

待補

G. Welcome to Join the Online Meeting!

Tags:

題目

  • 測資

待補

H. Subsequence Counting

Tags:

題目

  • 測資

待補

I. A Brand New Geometric Problem

Tags:

題目

  • 測資

待補

J. New Energy Vehicle

Tags: greedy

你在一維數線上開車,出發點是座標 ,有 個油箱分別能裝 公升的油。有 個加油站,第 個加油站在座標 處,並能夠幫你加滿第 個油箱。你的車每行駛 單位距離需要消耗 公升的油,問最遠可以開到哪裡(用完油剛好到加油站也可以加油並繼續行駛)。

  • 多筆測資、

待補

K. Farm Management

Tags:

給定 個工作 跟時間 ,請找到一個 schedule 使得

  • 是整數
  • 收益最大

你可以將至多一個工作修改成 ,求收益最大值。

待補

L. A Game On Tree

Tags:

題目

  • 測資

待補

M. Weird Ceiling

Tags: math

  • 測資數量

枚舉因數即可。

The 3rd Universal Cup. Stage 16: Nanjing

  • 連結:https://qoj.ac/contest/1828
  • 時間:2024 Nov 10, 02:00-07:00
  • 團隊:ネクロマンサー (SorahISA)
  • 成績:4 / 13, Penalty 565, dirt 0% ( A B C D E F G H I J K L M )

這場打的太混了 QwQ

整場都沒看計分板,因此一直覺得 L 可做,還有漏掉 G 是二元樹的性質。

Problem A. Hey, Have You Seen My Kangaroo?

待補

Problem B. Birthday Gift

Tags: observation

給定只包含 012 的字串 ,你可以不斷刪除 不為 0110 的 substring,問字串最短長度。

  • 多筆測資、

先考慮只有 01 的做法:可以 greedy 的遇到相同字元就刪掉(用 stack 維護)。

在這題也可以用類似的方法,維護把相鄰 0011 刪去的字串,會得到被 2 切開來,每段都是 01 交錯的字串。

而這就可以使用 DP,維護以 01 結尾的字串的所有長度,遇到 2 就將其換成 01(一樣當遇到連續相同字元就刪掉),因為可以構造出來的長度是連續的,於是可以只維護最小最大值。具體實作可以參考 submission

以下是題解給出的天才解法:

為把 所有偶數 position 的 01 字元反轉形成的字串,那 中刪掉 0011 就會變成 中刪掉 0110

一樣先假設沒有 2 的情形,可以發現只有當 字串全部字元相同時才不存在 0110,因此答案就是 01 的數量差。

如今我們只在意數量差了,那麼自然有 2 就可以往數量少的那邊補,這份 code 只有短短 8 行。

Problem C. Topology

待補

Problem D. Toe-Tac-Tics

待補

Problem E. Left Shifting 3

水題,跳過

Problem F. Subway

待補

Problem G. Binary Tree

Tags: interactive

待補

Problem H. Border Jump 2

待補

Problem I. Bingo

Tags: inclusive-exclusive, ntt

給你 ,你要將其由左上至右下填入 的表格 中。定義一個表格的 bingo 數

請計算所有 的排列的 bingo 數之和,模

min of max 很不好算,能不能把他換成只有 max 呢?

答案是可以的!用 min-max 排容:

以上算式的感性證明就是當 max 是第 小的時候你有 種其他東西的取法,而這些除了在 的時候都會正負消掉。

固定 ,並枚舉 中包含了幾個 row max、幾個 column max,可以得到以下算式:

其中 代表的是被 裡的 row 跟 column 覆蓋的格子數量,max of max 讓我們只需要計算這 個格子的 max 恰好是 的排列數量就好。

直接做是 ,得想辦法加速。令

那麼答案就是

把二項式拆開, 可以整理成如下形式,注意下標從 開始:

把只跟 有關的提出來變成 ,剩下的式子能整理成 的形式,感覺跟卷積很像了?

都是能預處理的,於是我們將 先 reverse 讓 裡面變成 的形式,讓 ,這題就能開心的用 NTT 解決掉了!

時間複雜度:

Problem J. Social Media

Tags: N/A

水題,跳過

Problem K. Strips

Tags: greedy

水題,跳過

Problem L.

待補

Problem M. Ordainer of Inexorable Judgment

待補

The 3rd Universal Cup. Stage 28: Haidian Huangzhuang

  • 連結:https://qoj.ac/contest/1903
  • 時間:2025 Feb 2, 02:00-07:00
  • 團隊:ネクロマンサー (SorahISA)
  • 成績:0 / 11, Penalty 0, dirt 0% ( A B C D E F G H I J K )

感冒所以只打快三個小時,但一題都沒做出來

Problem A. Iron Warrior

Problem B. Little, Cyan, Fish!

Problem C. Currency

Tags: min-cut

有一張 個點的網格圖,點的編號為 ),且 之間有一條帶正權無向邊當且僅當

接著有 個限制 ,表示若同時走了 兩條邊,則需要花費額外代價

請求出從 走到 的最小代價。

  • 權重

網路流最小割經典題。

首先,對每個 只會走其中恰好一個,否則路徑必然出現環。

因此,對 的邊建立一個點 。令走上方為 、走下方為 ,則可以建立 的邊並求出 。如果割掉 之後 相連,則代表 這條邊被選擇,反之亦然。

權重的設定也很簡單, 的權重就是下方該條邊的權重, 的權重就是上方該條邊的權重。會走直線 的情況只會發生在 不同邊(被切開了),於是可以在 雙向的建一條邊,權重就是直走邊的權重。不過要注意的是,可能會發生第一條邊走下面或最後一條邊走上面的情況,於是需要將方格左右各拓寬一格,讓 的權重設為

最後的 個限制,只要建立 權重為 的單向邊即可。該權重只有在 連著 連著 時才會被計算進去。

於是把 Dinic 模板貼上、按上所述的建好邊、跑 的最大流即可。

Problem D. Widely Known Problem

Problem E. Light Drinking and Low Singing

Problem F. Trash Problem

Problem G. Analysis

Problem H. Algebra

Problem I. Twenty-two

Problem J. Loving You in My Humble Way

Problem K. Ying's Cup

2025 ICPC Asia Pacific Championship

  • 連結:https://codeforces.com/contest/2073
  • 時間:2025 Mar 1, 10:00-15:00
  • 團隊:NYCU_MyGO!!! (SorahISA, ub33, nella17)
  • 成績:7 / 13, Penalty 1025, dirt 50% ( A B C D E F G H I J K L M )

A. Control Towers

Tags: math

給一個 的 grid,有些格子不能放置控制塔,請問有多少種放置四座相異控制塔的方法滿足以下條件:

  • 編號 的控制塔需要在同一行或同一列()。

解法

B. Three-Dimensional Embedding

Tags:

待補

解法

C. Cactus Connectivity

Tags:

待補

解法

D. Tower of Hanoi

Tags:

待補

解法

E. Minus Operator

Tags:

待補

解法

F. Hold the Star

Tags:

待補

解法

G. Corrupted File

Tags: greedy

給定 -字串 ,你可以不斷地選擇相鄰兩個字元合併成一個,合併後的結果是 操作的值。請問有沒有辦法變成字串

  • 多筆測資、

首先可以觀察到 中的 只能是 中的 只能是 中的

切開後整理成「每段連續 的長度」的形式(舉例來說: 會變成 ),以下稱之為

有幾個邊界 case 需要注意:

  1. 不能超過 ,因為每個 至少要吃掉一個
  2. ,因為 中第一段的 只能對上 的前面 吃不掉 ),最後一段同理。

接著就可以貪心的對 的第一個位置。

  • 可以 greedy 匹配的原因是 中連續的 只會匹配到 中連續的 ,且對 匹配到的位置一定在 匹配到的位置之前。
  • 也就是說要找到任意 滿足 ,是經典的 greedy 問題。

H. Secret Lilies and Roses

有一個隱藏的長度 的字串 ,其中只包含 兩種字元。請在 次詢問以內找到一個位置 滿足:

  • 中的 數量()等於在 中的 數量()。

你有兩種詢問操作可以做,分別是:

  1. 給定 ,詢問
  2. 給定 ,詢問
  • 多筆測資、

先觀察到 時會是 的數量,且 (因為一定是多一個 或少一個 )。目標 的位置就剛好是

如果能戳到第一個 或最後一個 得到他的 multi,就能直接知道某個

觀察 的性質:他會有一個前綴後綴是 、中間(可能為空)都是非

為了得到 跟非 的交界,我們可以嘗試搜出一個 substring,令 的位置為 ,則

  • 戳在 右邊所以是
  • 戳在 左邊所以是

而要在陣列中搜出 substring 是經典題(DOMJudge 測機題),可以用二分搜來做。假設 ,則可以二分搜尋 並更新左右界滿足區間是

注意特判搜到的 的情況。

詢問次數是二分搜的 加上詢問 次,總計 次詢問。

I. Squares on Grid Lines

Tags:

待補

解法

J. Gathering Sharks

Tags: dp

待補

經典區間 DP。

表示把編號 的人都移到編號 的位置的最小代價。則有轉移式:

按長度由小至大 DP 即可。

K. Book Sorting

Tags:

待補

解法

L. Boarding Queue

Tags: N/A

待補

對所有相鄰兩人 檢查:如果 代表 會走到 的位置,面對的是 ,判斷該數字有沒有在 即可。

M. Can You Reach There?

Tags:

待補

解法

2020 ICPC Asia Jakarta Regional Programming Contest

  • 連結:https://qoj.ac/contest/1019
  • 時間:2025 Mar 10, 12:00-17:00
  • 團隊:SorahISA
  • 成績:8 / 13, Penalty 1158, dirt 20% ( A B C D E F G H I J K L M )

4:03 過了 I 才第一次看計分板,發現 I 竟然場內滅台,場外也只有三隊 AC!?

A. Comic Binge

Tags:

meow

B. Moon and Sun

Tags:

meow

C. Cul-De-Sac Parades

Tags:

meow

D. Forbidden Card

Tags:

meow

E. Exchange Bottleneck

Tags:

meow

F. Shopping Changes

Tags:

meow

G. Permutation Transformation

Tags:

meow

H. Writing Tasks

Tags:

meow

I. Red Black Ball

Tags:

meow

J. Token Distance

Tags:

meow

K. Tree Beauty

Tags:

meow

L. Robust Defense

Tags:

meow

M. Police Stations

Tags:

meow

2024 ICPC Asia Seoul Regional Programming Contest

A. Bottles

Tags: prefix sum

個人在比賽,編號為 的人會在時間 從區域 出發,在時間 抵達區域

在不考慮整數點時間的情況下,請問每個區域最多同時有多少人?

水題,跳過

B. Cards Flipping

Tags: N/A

給定兩個長度 的序列 ,請對每個 選擇 使得 最大。

建圖,建了 的邊之後相當於是要對每條邊選周圍的一個點。

觀察到如果一個大小為 的連通塊是樹,那麼隨意定根並讓所有邊選擇較低的點就能做到

如果該連通塊有至少 條邊,那麼可以把邊拔到剩下 條變成基環樹,讓環上的邊互指,樹上的邊往下指就能做到

用 DSU 維護連通塊大小以及邊的數量即可,時間複雜度

C. Colorful Quadrants

Tags:

meow

  • meow

待補

D. Ladder Update

Tags:

meow

  • meow

待補

E. Mausoleum

Tags:

meow

  • meow

待補

F. Pair Sorting

Tags:

meow

  • meow

待補

G. Palindromic Length

Tags:

meow

  • meow

待補

H. Protecting Kingdom

Tags:

meow

  • meow

待補

I. Square Stamping

Tags:

meow

  • meow

待補

J. Street Development

Tags:

meow

  • meow

待補

K. String Rank

Tags:

meow

  • meow

待補

L. Triangle

Tags: N/A

給定一個頂點都在整數點的三角形 ,請求出在三條邊 上各選一個整數點(不含頂點)圍成三角形的最大面積 以及最小面積

輸出 即可,如果無解請輸出

  • 座標

顯然最大面積就是三角形的面積,但不能取頂點,所以要想辦法取盡量靠近的點;最小面積就是三條邊的中點連線,但中點不一定是整數點,所以要找到最接近的整數點。

上的所有點會是 的形式(其中 都是整數),而要滿足他是整數點就必須滿足 ,所以取

於是就可以在三條邊上分別取 四個點計算三角形面積即可。

時間複雜度

2024 ICPC Asia Taichung Regional Programming Contest

  • 連結:https://codeforces.com/contest/2041
  • 時間:2024 Nov 17, 09:30-14:30
  • 團隊:NYCU_MyGO!!! (SorahISA, ub33, nella17)
  • 成績:10 / 14, Penalty 874, dirt 9% ( A B C D E F G H I J K L M N )

A. The Bento Box Adventure

Tags: N/A

輸入四個 之間的相異正整數 ,請判斷 哪個數字沒出現過

輸出 即可。

B. Bowling Frame

Tags: N/A

個白色瓶子跟 個黑色瓶子。你要排成 排,滿足第 排有 個相同顏色的瓶子,請求出 的最大值。

  • 測資數量

待補

C. Cube

Tags: bitmask dp

找到三個 的排列 使 最大。

輸出該最大值就好。

待補

D. Drunken Maze

Tags: bfs

給定一個 的矩陣,其中恰有一個起點跟一個終點,且有些位置是不能行走的牆壁。你每次可以朝著四方位相鄰的格子移動一步,但是你不能連續四步走同樣的方向,請求出起點到終點的最短路徑長度(不存在則輸出 )。

  • 、邊界都是牆壁

一樣用 BFS 紀錄最短路即可,但狀態要多兩個維度紀錄走了哪個方向(大小為 )跟走了幾步(大小為 )。

  • 代表抵達 且此前 步的方向都是 的最短路徑長度。

只要從 queue 裡面 pop 出 就能直接輸出答案。

時間複雜度:

E. Beautiful Array

Tags: N/A

請構造一個長度介於 、數值界於 的整數陣列滿足其平均值為 且中位數為

輸出 即可。

F. Segmentation Folds

Tags: top-down dp, prime sieve

有一個區間 跟兩種摺疊操作

  • :選擇最大的 滿足 是質數,並將區間縮小為
  • :選擇最小的 滿足 是質數,並將區間縮小為

請輸出有幾種摺疊操作的序列可以使最後的區間長度最小。請輸出答案模 的值。

  • 測資數量

觀察到新的端點 乘以 之後是質數,於是問題可以被變化為以下模樣:

  • 初始區間是
  • :選擇 的最大質數當作新的
  • :選擇 的最小質數當作新的

於是可以先篩出區間 內的質數。由於每個合數 必定有一個 的質因數,所以透過預處理 以下的質數再對區間進行篩選(就跟一般質數篩很像,除了左界要調整到第一個 的倍數),就能在 時間篩出區間內所有質數。

考慮直接對其進行 top-down 的 DP。質數數量每一 fold 大約會變成一半,於是複雜度大約是

還不會好好證明複雜度,待補。

G. Grid Game

Tags: vbcc, implementation

有一個 的白色棋盤,其中有 個垂直的長條被塗成黑色的,保證白色的區塊四方位連通。請求出有多少個位置滿足該位置被塗黑之後白色的區塊不再四方位連通。

  • 測資數量

假設所有格子都被建出來,那麼直接在上面找割點就是答案了。不過本題的格子數量太多,所以要想辦法減少重要點的數量。

待補

H. Sheet Music

Tags: math

定義兩個長度皆為 個序列 本質相同,若且惟若對所有 皆有以下其中一種情況成立:

請問有多少個本質不同、長度為 、且每個元素都是 之間的正整數序列?

請輸出答案模

先假設沒有 的限制,那麼答案就是

再假設沒有 的狀況,那麼合法的序列就是所有沒有連續 的序列,這個序列的數量可以透過 DP 求得。

初始狀態為 、目標答案就是 ,可以用前綴和 計算。

現在考慮多出 。顯然 可以放在任何地方,所以如果有 ,那麼答案就是

時間複雜度:

I. Auto Complete

Tags: trie

meow

  • meow

待補

J. Bottle Arrangement

Tags:

給定 ,你可以將 的若干位置減一並將整個 重新排序,使得

  • 是 bitonic sequence)
  • for all

請輸出最少要對多少位置減一,如果全部都減一仍不可能達成,則輸出

  • 全部元素皆相異

待補

K. Trophic Balance Species

Tags:

給定一張簡單有向圖,定義一個集合 是獨立集若且唯若 中的點兩兩不可達。

定義 是從點 出發可以抵達的點的數量, 是可以抵達點 的點的數量。請求出 所有

  • 、最大獨立集大小

待補

L. Building Castle

Tags:

給定一個凸多邊形 ,請找到一個 旋轉對稱的凸多邊形 使得 (對稱差)的面積最小。

輸出該對稱差的面積即可。

  • 、座標皆是整數、沒有三點共線、誤差容許

待補

M. Selection Sort

Tags: N/A

給你一個長度為 的序列 ,你可以對前綴跟後綴分別各做至多一次排序(哪個先都可以,也可以不做),對 個元素排序的 cost 是 ,請求出最小的 cost 使陣列排好序。

首先可以把 離散化到 ,相同數字就以位置來排序。

先做前綴跟先做後綴的兩種狀況需要分開討論,不過先做後綴再做前綴剛好相當於對 先做前綴再做後綴。以下只考慮先做前綴的情況。

假設想要一次排好 數字 ,那麼前綴操作就要做到 位置,而因為 並沒有保證排好,後綴就要從 位置 開始排序來把數字 移到對的位置上。

不過想排好數字 不一定要做到 的位置。舉例來說,如果 ,那麼就只要排到 也能保證排好 的數字。

  • 想排好數字 可以只對前綴 排序,但想排好數字 就需要對前綴 排序。

實作上(submission)可以在由小到大枚舉 時用另一個變數紀錄當前結尾有多少數字滿足前綴 最大值是自己且自己也在正確的位置上。

時間複雜度:一開始需要 離散化,計算過程只要

N. Railway Construction

Tags:

有一張 個點的圖,其中有 條邊 被丟掉了,沒被丟掉的邊 的 cost 是

請對 求出:不使用點 的情況下的最小生成樹 cost,無解則輸出

待補

2024 ICPC Asia West Continent Final Contest

A. Basic Vocabulary

給定 ,請求出任意一組 滿足「 進位表示法」是「 進位表示法」的前綴。不存在這樣的 時,請輸出 -1

  • 測資數量

B. Bop to the Top

給定一個長度 的整數序列 ,你可以對他不斷執行以下兩種操作:

  1. 選定 ,把
  2. 選定 ,把

請輸出能否在有限次操作內使 變成等差數列。

  • 多筆測資、

等差數列就是差分都要相同()。以下定義

觀察到你可以對一段長度 的前綴或後綴 ,也就是能夠單獨調整除了 以外的所有 。又因為 只可能上升、 只可能下降,於是只要判斷 非空,即 就好。

時間複雜度:(瓶頸在輸入)

C. Fiendish Five

D. Modular Inverse Square Sum

E. Parker Rectangle

給定 ,請構造一個 的非負整數矩陣 ,滿足

  1. if
  2. 個和的全距

如果沒有滿足條件的矩陣,請輸出 -1

  • 多筆測資、

F. Seating Sweethearts

水題,跳過

G. Skill Issue

H. The Cabins in the Woods

I. Weightiest Watermelon

個點沒有對稱邊的簡單有向圖總共有 種。定義 如下:

給定 ,請求出所有 的總和,模 輸出。

  • 為質數

考慮分開計算每個點的貢獻,最後再乘上

一個入度為零的點可以向其他點任意連邊都不會破壞 DAG 的性質,於是要計算的值變成

個點的 DAG 數量,我們可以用如下方法計算得到:

其中,我們從這 個點以 種方法選擇 個點出來當作入度為零的點,然後這 個點可以向剩下 個點任意連邊,總共有 種方法。最後,剩下的 個點就變成了 。為了避免重複計算,要加上排容係數

預處理階乘逆元跟二的冪次直接計算就是 的了!

看起來有 的計算方法?ref

2024-2025 CTU Open Contest

  • 連結:https://codeforces.com/gym/105442
  • 時間:2024 Nov 1, 14:44-19:45
  • 團隊:NYCU_MyGO!!! (SorahISA, ub33, nella17)
  • 成績:9 / 12, Penalty 803, dirt 30% ( A B C D E F G H I J K L )

A. Flag Bearer

Tags: implementation

水題,跳過

B. Cowpproximation

個圓 ,求最小的 使得 非空。

  • 、誤差 以內。

經典題,對 二分搜之後檢查每個圓的圓周上是否存在一點在所有圓內。

兩圓交點弄出了一堆 bug,現在還沒 AC。

待補

C. Reptile Eggs

待補

D. Fishception

Tags: N/A

一開始平面上有 個點形成了一個矩形(邊不用平行座標軸),接著有 次操作,每次會加入 個點,這四個點會形成一個新的矩形,且新矩形嚴格包含了其他矩形們。

個平面上的點,代表所有操作後的點,求原本的矩形的面積。

每次拔掉凸包上的點, 次之後就會剩下原本的四個點。

  • Case 1: 最外面的矩形不平行座標軸
    • 座標最大、最小的點各不相同且必定在凸包上。
  • Case 2: 最外面的矩形平行座標軸
    • 座標最大、最小的點各有兩個,但容易看出這些點的集合只有四個點且必定在凸包上。

用兩個陣列維護按照 座標排序的 ID,剩下的用 while 迴圈就很好做。

E. Pigpartite Giraffe

待補

F. Hamster

Tags: N/A

水題,跳過

G. Pray Mink

Tags: dp

待補

範圍夠小,對效率沒有特別要求時用遞迴解會比較好做。

H. Ornithology

Tags: N/A

水題,跳過

求逆序數對可以用以下 code 搭配 BIT 來計算

int ans = 0;
for (int x : arr | views::reverse) {
    ans += bit.ask(x-1), bit.add(x, 1);
}

I. P||k Cutting

Tags: binary search, sparse table

給定非負整數 以及非負整數 ,求有多少個 subarray 滿足

OR 只會越變越大,可以以每個位置當左界做二分搜。

先用 sparse table 預處理區間 OR 就能做到

J. Rabid Rabbit

Tags: sweep line

給定正整數 以及 次詢問 ,求

其中 是費氏數列。

嘗試對每個 回答所有詢問。

滿足 ,如果把 打到二維平面上,那麼一個詢問 相當於是詢問存不存在 滿足

所有有意義的 可以用 map 對每個 找前面最後一個 的出現位置來求出。

把詢問跟 pair 依照左界( 座標)由大到小排序,只需要維護前綴 pair 的最小 座標即可 求解。

整體複雜度是

K. Fellow Sheep

Tags: N/A

有一個長度 的以下這種圖,給定 ,求左端點到右端點的最大流。

  • 值域

有四種不同路徑

對每塊取這四個 加起來就是答案。

L. Watchdogs

Tags: lca, minimum vertex cover

給一棵 個點的樹,有 隻老鼠在樹上,每隻老鼠只能在 路徑的重心(可能有一個或兩個)被抓到。

求最少需要在多少個點放置捕鼠器才能抓到所有老鼠。

求路徑中點就是經典 LCA 題。

每隻老鼠能被抓到的位置要嘛是一個點(可以看成自環),要嘛是樹上的一條邊連接著的兩個點。所以問題就變成要在樹上的某個子圖做點覆蓋。

可以樹 DP 就好,也可以直接砸 Dinic。以下是最大流的做法:

  • 因為樹是二分圖(考慮深度奇偶性),所以可以建如下的圖:
    • 源點連到深度為奇數的點
    • 深度為偶數的點連到匯點
    • 對老鼠能被抓到的邊,將深度為奇數的點連到深度為偶數的點
    • 對老鼠能被抓到的點,因為他一定要被選到,於是就新增一個虛點,並看奇偶決定怎麼連邊
    • 每條邊的流量皆為

Data Structure

Disjoint Set Union

支援操作:

  1. void init(int n)
    • 初始化點集合為 ,代表 -base 或 -base 皆可
  2. int R(int x)
    • 均攤 回傳 所在的集合的 boss 並路徑壓縮
  3. int U(int x, int y)
    • 均攤 合併 所在的集合,合併成功回傳 ,否則回傳
Code
struct DSU {
    
    vector<int> p;
    int maxn;
    
    void init(int N) {
        maxn = N + 1;
        p.resize(maxn), iota(ALL(p), 0);
    }
    
    int R(int x) { return x ^ p[x] ? p[x] = R(p[x]) : x; }
    int U(int x, int y) { return (x = R(x)) ^ (y = R(y)) ? p[x] = y, 1 : 0; }
    
};

Disjoint Set Union with Undo

支援操作:

  1. void init(int n)
    • 初始化點集合為 ,代表 -base 或 -base 皆可
  2. int R(int x)
    • 回傳 所在的集合的 boss
  3. int U(int x, int y)
    • 啟發式 合併 所在的集合,合併成功回傳 ,否則回傳
  4. void undo()
    • 回復上一次的有效 U(x, y) 操作
Code
struct DSU {
    
    vector<int> p, sz;
    vector<tuple<int, int, int, int>> ops;
    int maxn;
    
    void init(int N) {
        maxn = N + 1;
        p.resize(maxn), iota(ALL(p), 0);
        sz.assign(maxn, 1);
    }
    
    int R(int x) { return p[x] ^ x ? R(p[x]) : x; }
    
    int U(int x, int y) {
        x = R(x), y = R(y);
        if (x == y) return 0;
        if (sz[x] > sz[y]) swap(x, y);
        ops.eb(x, y, p[x], sz[y]);
        return sz[y] += sz[x], p[x] = y, 1;
    }
    
    void undo() {
        auto [x, y, p_x, sz_y] = ops.back(); ops.pb();
        p[x] = p_x, sz[y] = sz_y;
    }
    
};
Code with 離散化
struct DSU {
    
    vector<int> p, sz;
    vector<tuple<int, int, int, int>> ops;
    vector<int> disc;
    int maxn;
    
    void init(const vector<int> _disc) {
        disc = _disc, maxn = SZ(disc);
        p.resize(maxn), iota(ALL(p), 0);
        sz.assign(maxn, 1);
    }
    
    int _R(int x) { return p[x] ^ x ? _R(p[x]) : x; }
    int R(int x) {
        int id_x = lower_bound(ALL(disc), x) - begin(disc);
        return _R(id_x);
    }
    
    int _U(int x, int y) {
        x = _R(x), y = _R(y);
        if (x == y) return 0;
        if (sz[x] > sz[y]) swap(x, y);
        ops.eb(x, y, p[x], sz[y]);
        return sz[y] += sz[x], p[x] = y, 1;
    }
    int U(int x, int y) {
        int id_x = lower_bound(ALL(disc), x) - begin(disc);
        int id_y = lower_bound(ALL(disc), y) - begin(disc);
        return _U(id_x, id_y);
    }
    
    void undo() {
        auto [x, y, p_x, sz_y] = ops.back(); ops.pb();
        p[x] = p_x, sz[y] = sz_y;
    }
    
};

Interval Container

宣告:

  • IntervalContainer<int> itv;
    • 儲存 int 型別的資訊

支援操作:

  1. void cut(int p)
    • 切開(helper function,應該不會被直接呼叫)
  2. void modify(int l, int r, T v)
    • 區間的值修改為
  3. vector<pair<pii, T>> get(int l, int r)
    • 取得 區間的值
  4. vector<pair<pii, T>> remove(int l, int r)
    • 區間的值移除,並回傳被移除的區間與值
Code
template <typename T>
struct IntervalContainer {
    
    map<pii, T> itv;
    
    void cut(int p) { /// cut [p-1, p]
        auto it = itv.lower_bound({p, p});
        if (it == begin(itv)) return;
        if (it != end(itv) and it->first.first == p) return;
        it = prev(it);
        if (it->first.second < p) return;
        auto [lr, v] = *it; auto [l, r] = lr;
        it = itv.erase(it);
        it = itv.emplace_hint(it, pii(l, p-1), v);
        it = itv.emplace_hint(it, pii(p, r), v);
    }
    
    void modify(int l, int r, T v) {
        cut(l), cut(r+1);
        auto it_L = itv.lower_bound({l, l});
        auto it_R = itv.lower_bound({r+1, r+1});
        auto it = itv.erase(it_L, it_R);
        itv.emplace_hint(it, pii(l, r), v);
    }
    
    vector<pair<pii, T>> get(int l, int r) {
        cut(l), cut(r+1);
        auto it_L = itv.lower_bound({l, l});
        auto it_R = itv.lower_bound({r+1, r+1});
        vector<pair<pii, T>> ret;
        while (it_L != it_R) ret.eb(*it_L), it_L = next(it_L);
        return ret;
    }
    
    vector<pair<pii, T>> remove(int l, int r) {
        cut(l), cut(r+1);
        auto it_L = itv.lower_bound({l, l});
        auto it_R = itv.lower_bound({r+1, r+1});
        vector<pair<pii, T>> ret;
        while (it_L != it_R) ret.eb(*it_L), it_L = itv.erase(it_L);
        return ret;
    }
    
};

Tree Algorithm

Lowest Common Ancestor

支援操作:

  1. void init(int n)
    • 初始化點集合為 ,代表 -base 或 -base 皆可
  2. void add_edge(int u, int v)
    • 加無向不帶權的邊
  3. void build()
    • DFS + 建 Sparse Table
  4. int lca(int u, int v)
    • 的最近共同祖先
  5. int lca_jump(int u, int v)
    • 的最近共同祖先,需要把 anc[][] 相關的註解拿掉
  6. int jump(int u, int k) untested
    • 往上第 個祖先,需要把 anc[][] 相關的註解拿掉
  7. int dist(int u, int v)
    • 的距離

支援不連通的森林,但在做 LCA 時,請確保 連通

Code
struct LCA {
    
    vector<vector<int>> adj;
    // vector<vector<int>> anc;
    vector<vector<int>> sp_table;
    vector<int> dep, dfn_i, dfn_o;
    int lgmx, maxn;
    
    void init(int N) {
        maxn = N + 1;
        lgmx = __lg(maxn) + 2;
        adj.resize(maxn);
        // anc.assign(lgmx, vector<int>(maxn, -1));
        sp_table.assign(lgmx, vector<int>(2*maxn, 0));
        dep.assign(maxn, 0);
        dfn_i.assign(maxn, -1);
        dfn_o.assign(maxn, -1);
    }
    
    void add_edge(int u, int v) {
        adj[u].eb(v), adj[v].eb(u);
    }
    
    void build() {
        int tok_dfn = 0;
        function<void(int, int)> dfs = [&](int now, int lst) {
            // anc[0][now] = lst;
            dfn_i[now] = dfn_o[now] = tok_dfn;
            sp_table[0][tok_dfn++] = now;
            for (int x : adj[now]) {
                if (x == lst) continue;
                dep[x] = dep[now] + 1, dfs(x, now);
                dfn_o[now] = tok_dfn;
                sp_table[0][tok_dfn++] = now;
            }
        };
        for (int i = 0; i < maxn; ++i) { if (dfn_i[i] == -1) dfs(i, i); }
        // for (int lay = 1; lay < lgmx; ++lay) {
        //     for (int i = 0; i < maxn; ++i) anc[lay][i] = anc[lay-1][anc[lay-1][i]];
        // }
        for (int lay = 1; lay < lgmx; ++lay) {
            for (int i = 0, j = 1<<(lay-1); i < 2*maxn; ++i, ++j) {
                if (j >= 2*maxn) { sp_table[lay][i] = sp_table[lay-1][i]; continue; }
                int L = sp_table[lay-1][i], R = sp_table[lay-1][j];
                sp_table[lay][i] = (dfn_i[L] < dfn_i[R] ? L : R);
            }
        }
        
        // debug(dep);
        // debug(dfn_i);
        // debug(dfn_o);
    }
    
    int lca(int u, int v) {
        tie(u, v) = pii(min(dfn_i[u], dfn_i[v]), max(dfn_o[u], dfn_o[v]));
        int lay = __lg(v - u + 1);
        int L = sp_table[lay][u], R = sp_table[lay][v-(1<<lay)+1];
        return (dfn_i[L] < dfn_i[R] ? L : R);
    }
    
    // int lca_jump(int u, int v) {
    //     if (dep[u] < dep[v]) swap(u, v);
    //     for (int lay = lgmx-1, dif = dep[u] - dep[v]; lay >= 0; --lay) {
    //         if (dif >> lay & 1) u = anc[lay][u];
    //     }
    //     if (u == v) return u;
    //     for (int lay = lgmx-1; lay >= 0; --lay) {
    //         if (anc[lay][u] != anc[lay][v]) u = anc[lay][u], v = anc[lay][v];
    //     }
    //     return anc[0][u];
    // }
    
    // int jump(int u, int k) {
    //     for (int lay = lgmx-1; lay >= 0; --lay) {
    //         if (k >> lay & 1) u = anc[lay][u];
    //     }
    //     return u;
    // }
    
    int dist(int u, int v) {
        int c = lca(u, v);
        // assert(c == lca_jump(u, v));
        return dep[u] + dep[v] - 2 * dep[c];
    }
    
};

Lowest Common Ancestor with Information on Edges

支援操作(基本同上):

  1. void init(int n)
    • 初始化點集合為 ,代表 -base 或 -base 皆可
  2. void add_edge(int u, int v, const Info &info)
    • 加無向不帶權的邊 ,邊上帶有資訊 info
  3. void build()
    • DFS + 建跳表
  4. pair<int, Info> lca_jump(int u, int v)
    • 的最近共同祖先
  5. Info get_info(int u, int v)
    • 回傳 之間的邊上的資訊(只是 call 了一次 lca_jump
  6. int jump(int u, int k) untested
    • 往上第 個祖先
  7. int dist(int u, int v)
    • 的距離

需要自定義 struct Info,並實作

  • Info() (default constructor)
  • Info& operator += (const Info &rhs)
  • friend Info operator + (Info lhs, const Info &rhs)

此處的資訊是無向的,必須滿足交換律才能使用,否則還需稍作修改

Code
template <typename Info>
struct LCA {
    
    vector<vector<pair<int, Info>>> adj;
    vector<vector<pair<int, Info>>> anc;
    vector<int> dep, vis;
    int lgmx, maxn;
    
    void init(int N) {
        maxn = N + 1;
        lgmx = __lg(maxn) + 2;
        adj.resize(maxn);
        anc.assign(lgmx, vector(maxn, make_pair(int(0), Info())));
        dep.assign(maxn, 0);
        vis.assign(maxn, 0);
    }
    
    void add_edge(int u, int v, const Info &info) {
        adj[u].eb(v, info), adj[v].eb(u, info);
    }
    
    void build() {
        function<void(int, int)> dfs = [&](int now, int lst) {
            vis[now] = 1;
            for (auto [x, info] : adj[now]) {
                if (x == lst) continue;
                dep[x] = dep[now] + 1, dfs(x, now);
                anc[0][x] = {now, info};
            }
        };
        for (int i = 0; i < maxn; ++i) { if (vis[i] == 0) dfs(i, i); }
        
        for (int lay = 1; lay < lgmx; ++lay) {
            for (int i = 0; i < maxn; ++i) {
                int p = anc[lay-1][i].first;
                anc[lay][i].first = anc[lay-1][p].first;
                anc[lay][i].second = anc[lay-1][i].second + anc[lay-1][p].second;
            }
        }
        
        // debug(dep);
    }
    
    pair<int, Info> lca_jump(int u, int v) {
        if (dep[u] < dep[v]) swap(u, v);
        Info ans;
        for (int lay = lgmx-1, dif = dep[u] - dep[v]; lay >= 0; --lay) {
            if (dif >> lay & 1) ans += anc[lay][u].second, u = anc[lay][u].first;
        }
        if (u == v) return {u, ans};
        for (int lay = lgmx-1; lay >= 0; --lay) {
            if (anc[lay][u].first != anc[lay][v].first) {
                ans += anc[lay][u].second, u = anc[lay][u].first;
                ans += anc[lay][v].second, v = anc[lay][v].first;
            }
        }
        ans += anc[0][u].second, u = anc[0][u].first;
        ans += anc[0][v].second, v = anc[0][v].first;
        return {u, ans};
    }
    
    Info get_info(int u, int v) {
        return lca_jump(u, v).second;
    }
    
    int jump(int u, int k) {
        for (int lay = lgmx-1; lay >= 0; --lay) {
            if (k >> lay & 1) u = anc[lay][u].first;
        }
        return u;
    }
    
    int dist(int u, int v) {
        auto [c, info] = lca_jump(u, v);
        return dep[u] + dep[v] - 2 * dep[c];
    }
    
};

struct Info {
    
    int val;
    
    Info() : val(INT_MAX) {}
    Info(int _val) : val(_val) {}
    
    Info& operator += (const Info &rhs) {
        val = min(val, rhs.val);
        return *this;
    }
    
    friend Info operator + (Info lhs, const Info &rhs) {
        lhs.val = min(lhs.val, rhs.val);
        return lhs;
    }
    
};