BOI 2018 Day2 - Alternating Current
題目
有一個長度為個環形序列,上面有個線段,線段的左右界分別為,因為是環形區間,所以是允許的,現在請在每個線段塗上紅色或藍色,使的每個位置都至少被一個紅色線段與一個藍色線段覆蓋。
subtask
- ,Score 13
- ,Score 20
- ,Score 22
- ,Score 19
- ,Score 26
賽中我的作法
首先,將序列中每一個位置存下該位置被哪些線段覆蓋,接下來考慮以下case:
- 若存在一個位置覆蓋的線段數 < 2,那顯然無解
- 若某位置覆蓋的線段數 = 2,代表那兩個線段必須是一紅一藍,就在那兩個線段之間建一條邊
之後做一遍dfs,即可將所有有連邊出去的線段都先塗上顏色了
順便判一下有沒有奇環,有的話顯然無解
接下來對於剩下的線段就可以greedy填,看看一個位置有哪些顏色沒被塗到,就隨便選一個覆蓋該位置且尚未被塗色的線段塗色就好
以上方法複雜度為,滿分解的話可以運用跟TOI 2021 一模pD類似的方法
第一步可以使用一棵線段樹,存下對應區間被那些線段完全覆蓋
之後判斷線段數相當於一次query到根,沿途計算數量
再之後「判斷一個位置有哪些顏色沒被塗到」,以及「選一個覆蓋該位置且尚未被塗色的線段」一樣可以利用線段樹query一次到根,沿途好好維護該區間是否有線段被塗成紅色與藍色以及尚未被塗色的線段編號們
這樣一來均攤複雜度會是好的,
一切都相當合理,傳上去也會AC,然而這是個假解
反例的話可以自行構造看,我覺得構造的思考過程蠻有趣的XD
反例
,線段覆蓋的位置:
首先,dfs時會先將塗上藍,塗上紅
接著從位置1開始看,假設不小心將塗上藍,塗上紅
之後再看位置2,假設又不小心將塗上藍,這樣就掛了,因為位置全部都是藍色
修正
很明顯的是後面的greedy出了問題,戳了一些人的解之後發現了另一種greedy法:
首先一樣先做dfs,然後先將該塗的顏色塗好
接著先假設所有剩下的線段都是紅色,然後跑過每一條邊,若將該線段變成藍色,不會出錯(即為:區間每個位置的紅色數量皆 > 1),那就把它改成藍色,直到結束
證明
因為想很久都構不出反例,就試著證了一下正確性
由於greedy的策略,可以確定每個位置至少會有一個紅色線段,也因此我們只要證明每個位置至少會有一個藍色線段即可
Case1: 該位置沒有線段在dfs時先被塗上顏色
- 對於任意三個通過相同位置的線段,必存在一個線段使得他被另外兩個線段的聯集完全包含
首先,若這三個線段其中兩個已經完全包含了,那就顯然成立,否則線段會符合,如下圖

那這時也顯然成立,只要選第2條線段即可
- 在greedy塗色的過程中,任意位置的紅色線段數量最多為2
由於前面的結論,任三個線段必存在一個線段,需要其他兩個線段至少有一個被塗成藍色才可能被塗成紅色,所以不可能有三個通過相同位置的線段都被塗上紅色
由於greedy塗色的步驟中,每個位置的線段數 > 2,這代表至少會有1個藍色線段,因此證明完畢
Case2: 該位置有線段在dfs時先被塗上顏色
首先,要是線段被塗上的顏色為紅色,那就和前面的情況相同,紅色線段數的上界仍然為2,而若被塗上的顏色為藍色就顯然直接成立了
另外要說明的是一個位置在dfs時最多只會有兩個線段先被塗上顏色,原因是只有往左或往右兩種可能,因此不會有因為dfs就直接被卡住的問題
code
實作上前面判斷數量可以使用set + sweep line,而後面greedy可以使用一棵求最小值的線段樹,相較前面的假解實作輕鬆不少
1 |
|
後記
原本其實是想要證明自己賽中的解,結果在證明的時候越想越怪就構了個反例
後來翻到的解原本以為是錯的,想構反例但卻構不出來,就自己想了一個證明出來
希望證明沒有假解> <