AtCoder-abc345_f題解

lrx139發表於2024-03-17

題意簡述

給定一個無向圖。你要在其中選出一些邊,使得選出的邊所構成的圖中,度數為奇數的點有 \(K\) 個。如果可以,輸出選了哪些邊,否則輸出 -1

思路

首先在選一條邊時,邊兩端點度數的奇偶性一定都會改變,即要麼都變為奇數,要麼兩個點的奇偶性交換過來,要麼都變為偶數。這三種情況時滿足條件的點的個數分別變為 \(+2,0,-2\)。因此無論怎麼選邊,度數為奇數的點只能有偶數個。所以 \(K \equiv 1 \pmod 2\) 時一定無解。

題目沒說圖一定連通,所以我們可以對每個連通塊分別處理。所以我們只需要處理單個連通塊。

假設這個連通塊有 \(N\) 個節點,那麼這個連通塊在選邊後最多能有幾個點的度數為奇數呢?

答案是 \(N-(N \bmod 2)\),即不大於 \(N\) 的最大偶數。為什麼呢?

我們先求出這個圖的 DFS 樹。此時樹中有 \(N-1\) 條邊。在 DFS 時,同時維護子樹大小 \(sz\) 和搜尋時該點度數的奇偶性 \(li\)(奇數為 \(true\),偶數為 \(false\))。

我們在遍歷的時候,假設遍歷到了 \(u \to v\) 這條邊,\(u\) 的父親是 \(f\)。如果 \(li_v=false\),就選擇 \(u \to v\) 這條邊,並把 \(li_v\) 標記為 \(true\)\(li_u\) 反轉。便利完所有出邊後,會有下面幾種情況:

  • \(li_u=true\)

此時 \(u\) 已經滿足條件了。回到 \(f\) 時,\(f \to u\) 的這條邊不會被選。對 \(u\) 的搜尋結束,\(u\) 可以滿足條件。

  • \(li_u=false\)

此時 \(u\) 沒有滿足條件。那麼在回到 \(f\) 時,則會選擇 \(f \to u\) 這條邊,使得 \(u\) 滿足條件。

這麼看來所有的點都會滿足條件,除了根節點。如果 \(N \equiv 1 \pmod 2\),則除根節點之外的點都已經滿足條件,根節點無法滿足條件。否則根節點也可以滿足條件。

我們在搜尋的時候可以記錄還需要讓多少個點滿足條件。設這個值為 \(w\)。那麼我們在選邊的時候,根據上面遍歷到 \(u \to v\) 時的幾種情況再做出相應做法:

  • \(w=0\)

這條邊不選即可。

  • \(w>0\)

    • \(li_v=false\)

我們選擇這條邊,使得 \(v\) 滿足了條件,\(w \gets w-1\)

接下來我們計算出 \(li_u\),如果為 \(true\),則該點由不滿足條件變為滿足條件,\(w \gets w-1\)。否則該點由滿足條件變為不滿足條件,\(w \gets w+1\)

    • \(li_v=true\)

這條邊不選即可。

可以發現,在選擇一條邊後,\(w\) 值要麼不變,要麼 \(-2\)。這樣最後如果 \(w=0\),則有解,否則無解。

到這裡,最後一個問題就是記錄選擇哪些邊了。我們可以使用鏈式前向星存圖,因為是無向圖,每個邊存兩次。我們可以從編號 \(2\) 開始存,第一條邊的編號為 \(2,3\),第二條為 \(4,5\)。我們再用一個 \(ans\) 陣列,如果這條邊選了,我們把對應邊編號的 \(ans\) 值設為 \(true\)。這樣每條邊編號除以二下取整就是它實際邊的編號。

那麼這個問題就解決了。

程式碼