JOISC 2021 部分題解
為了讓題解集合不顯得太臃腫,程式碼託管到了第三方上面。
Day1
特技飛行
又是提答又是計算幾何,註定很少人會去寫它
IOI 熱病
咕掉惹。
飲食區
考慮如果我們可以去除“離開”操作的影響,假裝沒有人離開並修改“服務”時候的查詢引數,那麼也可以解決問題。
假如沒有人離開,那麼就可以使用整體二分解決這個問題。
現在有人離開,假如不存在某次佇列全部清空的情況,那麼可以直接讓詢問的 \(B\) 加上離開人數從而去掉離開的影響。
假如存在清空的情況,那我們需要統計實際離開人數。假設對於商店 \(A\),目前有 \(n\)
由於 \(\delta\) 是按照時間排序的,因此可以按照時間,用線段樹維護每個位置上人數變化的字首和最小值。知道了實際人數,也就不難算出“服務”的具體引數了。
Day2
逃跑路線
目前只能在 JOISC 官網上面下載題目檔案。OJ 上還沒有測評包。
道路建設*
鑑於 \(K\) 比較小,因此可以想到用堆維護每個點的最近點對,並按順序取出前 \(K\) 小的點對。
為了保證不會重複計算,對於標號為 \(i,j(i<j)\) 的點,我們只會在 \(j\) 上計算 \((i,j)\) 的點對。
一個比較**的做法是,將曼哈頓距離轉化為切比雪夫距離,那麼查詢最近點對可以使用二分+二位數點,可惜是 \(O(n\log_2^2n)\) 的。
另一個更加厲害的做法是,直接用 KDT 維護最近點對!這個我不清楚啊,看別人寫的
真正的做法:
由於計算點對已經有了有序性,利用這個有序性我們可以順便拆掉一個絕對值。比如,將所有點按照 \(x\) 排序就可以去掉 \(x\) 上的絕對值。那麼我們只需要處理 \(y\) 一維的絕對值,這個按 \(y_1,y_2\) 關係分類後利用線段樹完成。
考慮到每次加入點和每次刪除點對都只會修改一個位置,我們可以使用可持久化線段樹維護字首可用點。
刪除需要注意。最值本身不支援直接刪除,但考慮線段樹的葉子上已經保證了 \(y\) 相等,而 \(x\) 又是從小到大有序的,所以對於一個葉子而言,該位置上的前一個版本的資訊一定就是刪除該點對之後的最大值,所以讀取前一個版本的資訊即可。
小結:
- 看到“絕對值”這樣的資訊,應該要注意分類討論,而非盲目地轉化成切比雪夫距離;
- 主席樹刪除用到的小技巧可以注意一下。
購物
目前只能在 JOISC 官網上面下載題目檔案。OJ 上還沒有測評包。
Day3
古老的機器*
人生中第一道通訊題,獻給了 JOISC 2021 的這道題目
30 pts
首先需要知道如何不得到 Wrong Answer [6]
:
考慮最左邊的一個 \(X\),如果移除順序合理,它是可以被共享的。
將該 \(X\) 之後的序列按照 \(Z\) 分段,並且分段處理,每一段在處理完之後需要保證完全被移除。那麼,每一段上就只可能有 \(X\) 或者 \(Y\)。對於連續的一段 \(Y\),選取其中的任意一個留在最後移除效果都一樣;而對於連續的一段 \(X\),我們可以讓它們在右邊的機器全部被移除之後再移除它們,一定是最優的。
為了簡化操作過程,我們從左往右移除段,每一段上先從右往左逆向移除 \(X,Y\),最後再將 \(Z\) 刪除。可以發現這個過程是符合上面的分析的。
Anna 只需要每兩位描述一個機器的型號,將資訊傳給 Bruno,剩下的交給 Bruno 就可以獲得該部分分。
現在的空間利用比為 1 : 2。
40 pts
由於每一位只有 \(3\) 種情況,使用 \(2^2\) 的狀態描述太浪費了。
注意到 \(\log_2 3^n=\log_2 2^{n\log_2 3}\approx1.6n\),因此我們可以代入得到 \(3^5\) 應該與 \(2^8\) 接近。
實際上也確實如此,\(3^5\) 略小於 \(2^8\),因此我們可以用 8 位二進位制壓縮 5 位資訊。只需要利用 \(1.6n\) 位即可完成任務。
現在的空間利用比為 5 : 8。
70 pts
Bruno 實際上只需要知道第一個 \(X\) 的位置和之後的 \(Z\) 各自的位置就可以知道如何操作。我們可以將這些位置用 1 標註,這樣他就可以自己識別了。
現在的空間利用比為 1 : 1。
100 pts
這個優化就比較厲害了
考慮到連續的一段 \(Z\) 實際上意義也不大,除了最右端的 \(Z\) 以外前面的 \(Z\) 也沒啥用。那麼我們就可以只在最後的這個 \(Z\) 上做記號。
這樣的傳輸資訊有啥特點呢?很重要的性質就是除了第一個 \(X\) 與第一個 \(Z\) 以外,剩餘的 1 不可能相鄰。
這樣的序列的數量是多少呢?通過各種方法可以算出,長度為 \(n\) 且無相鄰 1 的序列數量為 \(f_{n+2}\),其中 \(f_k\) 為 Fibonacci 數列的第 \(k\) 項。
嘛,\(f_{n+2}\) 肯定還是會比 \(2^n\) 小一些的。打表觀察又可以發現 \(f_{63+2}\) 略小於 \(2^{44}\),因此我們可以選擇用 44 位二進位制壓縮 63 位資訊,那麼只需要使用約 \(\frac{44}{63}n\) 即可解決問題。
有一個小細節,為了避免第一個 \(X\) 與第一個 \(Z\) 的 1 連續,我們可以在第一個 \(X\) 之後插入一個 0 來處理。這樣基本不會影響整體操作。
現在的空間利用比為 63 : 44。
小結:
-
壓縮資訊的通訊題目,目的就在於提高空間利用比。
對於 Anna 而言,作為資訊發出端,她需要做的是:
-
讓同等長度內的字串裝下儘可能多的資訊。
例如,從 1 : 2 提升到 5 : 8 的過程中,原先的 \(3^4\) 裝進 \(2^8\),顯然沒有 \(3^5\) 裝進 \(2^8\) 划算。
由於字串的資訊通常是指數級的,因此這通常是在函式上找整點的問題。
-
通過優秀的編碼方式來表示資訊。
例如,從 1 : 1 提升到 63 : 44 的過程中,已有的 \(2^n\) 對於有效資訊來說還是浪費;給有效資訊合理編號從而壓縮標號的值域才能成功地減少所需長度.
對於 Bruno 而言,作為資訊接收端兼操作端,他需要做的是:
-
明確如何操作。
-
簡化操作從而簡化需要的資訊。
同樣是 1 : 1 提升到 63 : 44 的過程中,部分 \(Z\) 的資訊已經被省略掉了,才能相應地減少有效資訊。
-
-
任何有意義資訊的基礎都是有效編碼。在二進位制字串中,可用碼很少,因此需要充分利用字串的附加資訊,例如編碼格式。所以在實際操作過程中一定需要注意設計好格式。對於字串而言,字串內容是明面資訊,而字串格式更是重要的隱含資訊。
保鏢
又咕掉惹
聚會 2
為了方便虛樹,下面稱居住點數目為 \(k\) 時的答案為 \(f_k\)。
以出席者的居住點構建虛樹,那麼滿足條件的開會點就是樹上帶權重心。
如果帶權重心是一個點那沒什麼可說的;如果是一條邊 \((u,v)\),那麼滿足條件的點的數量為 \((u,v)\) 兩點在原樹上的距離 +1 。
什麼時候重心在邊上?當然是這條邊左右子樹大小一樣的時候。回到原樹上來,如果 \((u,v)\) 想要成為虛樹上的重心,那麼就必須存在一種方案使得以路徑 \((u,v)\) 為根的時候,\(u\) 的子樹內和 \(v\) 的子樹內可以選出同樣多的居住點來。
設此時 \(u\) 子樹大小為 \(s_u\)。上述條件意味著 \((u,v)\) 可以貢獻到 \(f_{2k}\),當且僅當 \(k\le \min\{s_u,s_v\}\)。
那麼問題就方便多了。對於 \((u,v)\),我們可以將它貢獻到 \(g_{\min\{s_u,s_v\}}\) 去,那麼 \(f\) 就可以由 \(g\) 在 \(O(n)\) 的時間內計算出來。
此外,由於 \(u\) 的子樹大小取決於 \(u,v\) 之間的相對關係,我們需要分類討論:
-
如果 \(u,v\) 之間存在祖孫關係:
這個時候不妨設 \(u\) 是 \(v\) 的祖先,唯一需要注意的是 \(u\) 的子樹大小會發生改變。
我們在 \(s_u\) 成為較小值的時候統計一遍 \((u,v)\) 的資訊,此時是按照 \(s\) 的大小查詢子樹資訊;
我們還在 \(s_v\) 成為較小值的時候統計一遍 \((u,v)\) 的資訊,此時可以逆序列舉 \(s\) 並進行祖先查詢;
-
如果 \(u,v\) 之間不存在祖孫關係:
那麼 \(u,v\) 的子樹大小都不會有變化。
可以在 \(\operatorname{LCA}(u,v)\) 處統計資訊。那麼就不難想到設 \(dp_{u,i}\) 表示 \(u\) 的子樹大小為 \(i\) 的子孫的最大深度。
轉移可以直接線段樹合併,貢獻相當於做了一次 \(\min\) 卷積,維護一下字尾最大即可。
注意:線段樹合併的時候,遇到了空結點需要將已有的字尾資訊在非空結點上打上“貢獻”標記,避免漏掉貢獻。
小結:
- 一定要想清楚之後再開始寫程式碼,不要急急忙忙導致反覆重構!
- 線段樹合併的時候不要漏掉了貢獻。
Day4
活動參觀 2*
求字典序最小不難想到從小到大列舉每個活動是否能加入到“必選”活動中。
如何檢查?考慮當前活動時間為 \([l,r]\),如果它不與其他必選活動相交,那麼它就會被包含在空閒時間段 \([L,R]\) 中,也即 \([l,r]\subseteq [L,R]\)。此時 \([L,R]\) 會被拆分成兩個新的時間段 \([L,l]\) 和 \([R,r]\),因此我們只需要能夠快速求出任一空閒時間段的最大活動數即可。
一般我們會採用 \(O(n)\) 的貪心處理。但是,貪心一般意味著一些較簡單可疊加的操作。例如,詢問空閒區間 \([p,q]\) 時,我們會在可用活動中選取右端點最小的,例如 \([a,b]\),之後的操作就相當於繼續對 \([b,q]\) 進行詢問。
由於 \([a,b]\) 的選擇與 \(p\) 有較大關聯,因此我們可以設 \(f_p\) 為滿足 \(p\le a\) 的活動中的最小的 \(b\)。那麼詢問就相當於反覆跳 \(f_p\),因而可以使用倍增優化。於是單次查詢就變為了 \(O(\log_2n)\)。
小結:
-
對於倍增的適用範圍要足夠熟悉,它通常是處理一些相似、重複的行動,例如指標跳轉。
由於部分貪心操作相對簡單,倍增也常常可以用於優化貪心;
-
應當分析一些貪心操作的本質;
嚮導 2
目前只能在 JOISC 官網上面下載題目檔案。OJ 上還沒有測評包。
最差記者 4
草系列題目怎麼都出到 4 了
考慮從 \(i\) 向 \(A_i\) 連邊,最後一定可以得到一棵基環內向樹。
這裡列舉一些簡單的性質:
- 根據邊的含義可以知道環上的點最終 rating 相等;
- 修改後的 rating 總可以是初始 rating 之一,因此初始 rating 是可以離散化的;
- 我們可以反過來求沒有修改的選手的 \(C\) 之和的最大值,下文稱“沒有修改的選手的 \(C\) 的和”為“貢獻”;
考慮特殊的樹的情況:樹上就可以 DP 了,比如設 \(f_{u,i}\) 表示當 \(u\) 的 rating 為 \(i\) 時的最大貢獻。
可以目測出 \(f\) 的轉移為:
\[f_{u,i}=[i=H_u]C_u+\sum_{v}\max_{i\le j} f_{v,j} \]如果暴力做是 \(O(n^2)\) 的。
注意到轉移中複雜的地方就是 \(\max_{i\le j} f_{v,j}\),如果我們能快速維護它就好了。
為此,我們設 \(g_{u,i}=\max_{i\le j}f_{u,j}\),並研究一下它的性質:
-
\(g_{u,i}\) 隨 \(i\) 增大而不增;
-
\(g_{u,i}\) 是分段的;
-
如果將 \(g_{u,i}\) 和 \(g_{v,i}\) 對應位置加起來:
其中的藍色實線表示 \(g_v\),紅色虛線表示 \(g_u\),紫色點線表示 \(g_u+g_v\)。
可以發現 \(g_u\) 和 \(g_v\) 的差分也是對應相加的,並且與一般的 \(g\) 一樣也具有相似形式。
-
在轉移中,\(\sum_{v}g_{v,i}\) 是具有相似形式的,我們考察在 \(H_u\) 的位置單點加上 \(C_u\) 之後對 \(g_u\) 的影響:
可以發現此時是從 \(H_u\) 開始向前,將一段推平。如果考慮 \(g_u\) 的差分,那麼相當於找到最大的 \(l\),使得 \([l,H_u)\) 的差分之和大於 \(C_u\),之後便會將 \((l,H_u)\) 的差分推為 0,並且修改 \(l,H_u\) 的差分值。
通過上述分析,我們可以發現 \(g\) 的差分可以很方便地使用線段樹維護。
這便是樹上的解法;基環樹上也並不複雜。將環上的每棵樹都做一遍 DP 之後合併。由於這裡的 \(f\) 並沒有做字尾 \(\max\),所以需要列舉最終環上的權值並計算最大貢獻。
時間複雜度可以是 \(O(n\log_2^2n)\),也可以是 \(O(n\log_2n)\)。
小結:
- 運用函式思維、幾何方法,分析 DP 陣列的性質從而利用資料結構高效維護;
- 補集轉化的思想,大大簡化了問題;