連續被幾個同學吊打 \(10\) 場了,這次總算抬了下頭(很明顯是運氣因素,下次又要被吊打了)。
場切 A
B
C
,補了 D
E
,F
放一放,要莫隊(聽說這東西只有東方神秘的中國 OIer
才會)。
賀了 D
和 E
兩題的官方題面圖片幫助理解題意。
CF1508A Binary Literature
神似某場 AGC 一道我場上做了約 \(100 \min\) 最終還是沒有做出來的 A
題,但是簡單得多。
把三個串分為 1
的個數 \(\ge n\) 和 0
的個數 \(\ge n\)(如果都滿足就是前者好了)。
根據抽屜原理,很明顯會有一個類有至少兩個,不妨設為前者。
搞兩個串中 1
的數量的最大值個 1
,在相鄰兩個 1
之間塞兩個串同個位置之間的 0
的數量的最大值個 0
即可(最左邊的 1
左邊和最右邊的 1
右邊同理)。
CF1508B Almost Sorted
雙管齊下:先找個規律,發現長度為 \(n\) 的有 \(2 ^ {n - 1}\) 種。
然后總結一下這個排列的規律:發現 \(n\) 個數被划分成若干個區間 \([l, r)\),並且每個區間正好是 \([l, r)\) 這段連續的排列翻轉過來(比如 \([3, 7)\) 這段區間就是 \(\{6, 5, 4, 3\}\))。
然后就可以用排列求字典序第 \(k\) 大的常見套路了,不過要便便。原來是依次枚舉每個數,現在依次枚舉每個區間。
然后還有一個小問題,就是 \(2 ^ {n - 1}\) 很可能爆 long long
,如果這樣直接搞成 \(2\times 10 ^ {18}\) 即可。
CF1508C Complete the MST
雖然做出來了,但是 \(6\) 發罰時讓我錯過回紅!以后再也不用帶 exit(0)
的調試語句了!
異或和為 \(0\) 很明顯就是忽悠人的把戲,可以分為兩種情況討論:
- 沒確定權值的邊有 \(\ge n\) 條,那么至少一條邊不用,所以可以用那條邊平衡異或和,別的邊邊權都是 \(0\),實現的時候直接默認所有沒有給出的邊權值都是 \(0\) 即可。
- 否則 \(\frac{n(n - 1)}{2} - m < n\Longrightarrow m > \frac{n(n - 3)}{2}\)。那么 \(n \le 700\)。可以暴力。
然后講講上面兩種情況的具體實現:
對於第一種,可以並查集和 stl::set
或者 FenwickTree 先把所有反圖連通塊給搞出來,然后用給定邊在這個並查集上跑 Kruskal 即可。時間復雜度 \(\Theta((n + m)\log n)\)。
我用的是 FenwickTree,對於每個連通塊,從一個任意點開始蔓延:
維護兩個集合(stl::vector
),一個是當前連通塊所有點的出邊交,另一個是當前連通塊的所有點。
很明顯兩個集合沒有交集,如果一個點不在它們的並集中,那么就可以加入當前連通塊。
用 FenwickTree 維護兩個集合的並集,可以用樹狀數組二分(\(\Theta(\log n)\))求出還有沒有可以加入當前連通塊的點以及如果有編號最小的這樣的點是什么。
對於第二種,可以先用給定邊跑一次 Kruskal 得出大小為 \(\Theta(n)\) 的有用邊集。
然后對於每條未給定邊(最多 \(n - 1\) 條),把他的權值改為所有給定邊權值的異或和,然后把所有給定邊和未給定邊跑一次 Kruskal 然后求最小生成樹權值最小值即可,時間復雜度 \(\Theta(m\log n)\)。
CF1508D Swap Pass

貌似每次這種交換兩個恢復排列的題都是“必然有解,一個置換怎么做,多個置換怎么做”。
首先必然有解(可以先這么假設,然后下面給出構造),然后可以忽略 \(a_i = i\) 的所有 \(i\)。
然后考慮一個置換怎么做:隨意選一個點,不停交換 \(a_i\) 和 \(a_{a_i}\) 直到 \(a_i = i\) 即可。
那能不能每個置換都這么做呢?由於置換不影響幾何位置,所以這樣可能會有兩個置換之間的交換相交。
容易發現,如果交換兩個來自不同置換的點,那么兩個置換就合並了。
然后就是要開發腦洞再固定一個點當作原點,把別的點(\(a_i\neq i\))極角排序,設總共有 \(m\) 個點。
只交換極角排序相鄰(包括首尾)且夾角 \(< \pi\)(這里我想了好久沒想到,我果然對計算幾何不熟悉啊!一個上午就這么沒了)的兩個點的至少可以有 \(m - 2\) 次交換,由於每個置換的點數 \(\ge 2\),易證只通過這些交換可以把所有置換合並,且交換不相交。
然后對於剩下的這 \(1\) 個置換,把選的原點當做選定點不停交換即可,易證沒有交換相交。
CF1508E Tree Calendar

少看了“字典序最小”這個條件,我的一個下午啊(/wul/ll)。
那么對於一棵樹的操作路徑是固定的,且每個點 \(u\) 上的數都是往下移到一個子樹順序最小且極低的還沒有標記的節點,然后標記這個節點。
觀察最終的序列會發現新的序列是子樹順序和老的序列(入棧序)一樣的樹的出棧序。
而且很容易發現,對於任意一個節點,無論怎么操作,子節點(就是和它相連的點)上的數的相對大小都是不會變的(比如原來是 \(2, 3\),現在很可能就是 \(1, 3\) 或 \(1, 2\)),所以可以得出原樹的子樹順序以及原序列。
然后開始處理得到的序列。如果根節點上的數是 \(x\),那么此時正在移動的必然是 \(x - 1\),可以先把 \(x - 1\) 恢復到根節點(不停和父親節點上的數交換),這樣就沒有正在進行的移數流程了。這時候要判斷原來 \(x - 1\) 要到的位置是不是在原來 \(x - 1\) 的位置的子樹里,如果不是就 NO
,否則當前問題就和原問題等價了。
否則,如果這個樹的序列是合法的,那么所有 \(a_u < x - 1\) 的點 \(u\) 必然是移好的,否則必然是沒移好的。只需要把移好的和出棧序對比,未移好的和剩下的樹(刪去移好節點)的入棧序對比即可。如果不同就輸出 NO
。否則就是 YES
,移動次數是所有移好的的深度之和,要求的初始序列就是初始入棧序。