題意簡述
給定一個無向圖。你要在其中選出一些邊,使得選出的邊所構成的圖中,度數為奇數的點有 \(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\)。這樣每條邊編號除以二下取整就是它實際邊的編號。
那麼這個問題就解決了。
程式碼