代碼自己去LOJ看
JOI2013
彩燈
把序列划分成若干極長交替列,那么最優的方案一定是將一個極長交替列翻轉使得連續的三個極長交替列合成一個。計算相鄰三個極長交替列長度的最大值即可。
搭乘IOI火車
DP:\(f_{i,j}\)表示兩個串的起點分別在\(i,j\)位置最長的拼接長度,轉移就先放一個'I',再放一個‘O’看能夠轉移到哪些位置。
現代豪宅
把起點、終點和所有有開關的點建兩個點分別表示南北和東西方向,\(x\)相同、\(y\)相鄰的兩個點南北方向連距離的邊,\(y\)相同、\(x\)相鄰的兩個點東西方向連距離的邊,每個有開關的點南北方向和東西方向之間連1邊表示切換方向,然后跑最短路。
JOIOI塔
從后往前考慮。O一定放在一個准備做基底的I上面,J一定放在一個'OI'上面,但對於I,既可以放在'OI'上面,又可以做基底。不難發現當基底需求量確定時,如果當前基底數量小於需求量,則放基底更優,否則放在'OI'上更優。於是二分基底需求量,也就是二分答案,然后跑一遍模擬一下上述操作看能否合成對應多個塔。
冒泡排序
首先不難注意到選擇交換的左端點是前綴最大值、右端點是后綴最小值時更優。我們把這兩個序列求出來,這兩個序列都是單調遞增的。
對於一個位置,我們考慮它能夠產生的貢獻。首先交換的左右端點得越過它。當左端點權值\(>\)當前點權值、右端點權值\(<\)當前點權值時,這個位置能夠減少\(2\)的逆序對;當其中有一個相等時,可以減少\(1\)的逆序對;否則沒有影響。注意到能夠減少逆序對的左右端點在前綴最大值、后綴最小值數組上都對應一段區間,那么一個位置相當於矩形+一個數,然后求平面的最大值,用掃描線+線段樹統計。
JOI2014
JOI徽章
枚舉替換的位置和替換的字符,處理最多4個矩形對答案的影響。
IOI饅頭
因為當總容量確定之后一定選價值最高的那些饅頭,於是背包求出每種容量的最小代價即可。
年輪蛋糕
先斷環成鏈,變成形如若干個將\([i,i+n-1]\)分成三段使得三段和最小值最大的問題。
注意到當詢問由\([i,i+n-1]\)變到\([i+1,i+n]\)時,分成的三段的第一段的右端點一定不降。於是我們維護第一段的右端點,每一次check是否右移就二分第二段的右端點。
飛天鼠
當\(X=0\)時,最優策略一定是每一次爬到恰好能去到某棵樹的高度然后飛過去,也就是到達每棵樹時\(X\)都是\(0\)。先從\(N\)最短路跑出在\(X=0\)的情況下每個點到\(N\)的最短時間。
現在初始\(X\)不為\(0\),那么不難發現性質:在最優路徑中一定存在一棵樹滿足從初始位置到達這棵樹一直沒有上升,從這棵樹開始需要上升,然后基於\(X=0\)的策略行動。故從\(1\)再跑出從\(1\)開始在不上升的情況下到達每棵樹時的最大的\(X\),最后枚舉這樣的樹是哪棵就行了。
裁剪線
把平面看成無限大,四個邊界也看成裁剪線。然后以\(y\)坐標掃描線,外部維護一個並查集,掃描線的過程中用一個數據結構維護以當前\(y\)值橫截得到的所有段的左端點(最左邊一段的左端點就是-INF)和這些段在並查集中對應的編號。一個與\(y\)軸平行的線加入就是把一個段分成兩個,它們在並查集上對應的點相同;一個與\(y\)軸平行的線刪除就是把兩個段合成一個,並將它們在並查集上並在一起。
與\(x\)軸平行的線段加入相當於把這條線段覆蓋的段在並查集上建立新點。顯然暴力是不行的,考慮在數據結構上支持區間更新標記,並維護區間被更新但是沒有在並查集上建立新點的段的數量。因為對於“被更新但是沒有在並查集上建立新點的段”,它們沒有在更新之后的任何操作中被用到,所以如果這個段再次被與\(x\)軸平行的線段覆蓋,那么兩次\(x\)軸覆蓋之間的塊一定單獨成連通塊。所以我們每一次在區間上打標記的時候,區間被更新但是沒有在並查集上建立新點的段的數量也要貢獻答案。
最后算一下並查集里面的集合數量,減掉最外圍的無限大連通塊就是答案。
注意當同一個縱坐標同時存在\(y\)軸加入、\(y\)軸刪除、\(x\)軸加入時,處理順序應該是\(y\)軸加入\(\rightarrow\)\(x\)軸加入\(\rightarrow\)\(y\)軸刪除。
JOI2015
鐵路旅行
差分求出每一條鐵路的經過次數。
分蛋糕2
斷環成鏈,設\(f_{i,j}\)表示IOI先手、蛋糕左端點為\(i\)、右端點為\(j\)時JOI能夠拿到的最多的蛋糕,然后枚舉JOI最開始選哪個蛋糕。
JOI公園
先跑出1到其他點的最短路。因為最優方案中\(X\)一定是某個點的最短路長度,於是從小到大枚舉\(X\),維護兩端最短路均\(\leq X\)的邊的數量。
舞會
先二分答案,轉化為序列中元素是\(01\)的問題。設\(f_i\)表示讓第\(i\)個位置的人為\(1\)的最小代價,初始值當一個位置不確定時為\(1\),確定時如果當前位置為\(1\)則初值為\(0\),否則為\(INF\)。那么每一次從隊頭丟掉三個數、從隊尾加入一個數,隊尾加入的\(f\)值應該是隊頭丟掉的三個數的\(f\)值的較小兩個的和。最后看一下隊列中剩下的\(f\)是否小於等於當前沒有確定的為\(1\)的人,就可以知道當前但是否合法。
城牆
設\(rd_{i,j}\)表示位置\((i,j)\)向右、向下的第一個障礙物到\((i,j)\)距離的較小值,\(lu_{i,j}\)表示位置\((i,j)\)向左、向上的第一個障礙物到\((i,j)\)距離的較小值。
那么對於一個滿足條件的框的兩個對角\((i,j)(i+p,j+p)\)需要滿足\(rd_{i,j} \geq p+1 , lu_{i+p,j+p} \geq p + 1\)。也就是說一個點\((i,j)\)作為右下角可以匹配邊長\(\leq lu_{i,j}\)的框,作為左上角則可匹配邊長\(\leq rd_{i,j}\)的框。
枚舉\(j-i\)的值,把所有位置拿出來一起算答案,從底往上用樹狀數組記錄下能夠與當前位置匹配的位置,每一次查詢一段區間內的位置數量貢獻答案。
JOI2016
橙子裝箱
設\(f_i\)表示裝前\(i\)個橙子的最小費用,從小到大枚舉\(f_i\)向外的轉移點維護\(a,b\)進行轉移。
集郵比賽2
J放最前面、I放最后面最優,對於O計算出J的前綴和和I的后綴和,枚舉位置可直接算答案。
鐵路票價
先跑出最短路DAG,隨便拿一棵最短路樹出來,記下每個點的父親。
一次修改如果改樹邊沒有影響,如果改樹邊考慮在DAG上是否存在能夠替換這條樹邊的邊。如果有則替換並更新父親,否則這條樹邊連接的最短路較長的點的最短路會增大。然后再遞歸處理最短路樹上是這個節點的兒子的節點。
領地
先特判走一輪之后走回\((0,0)\)的情況。
假設走一輪之后JOI從\((0,0)\)到了\((px,py)\)。先通過將操作方向取反把\(px,py\)都弄成非負,然后對於一個被占領的正方形\((x,y)\)(這里是指\((x,y)\)是正方形的左下角),如果其四角第一次被經過時輪數最小值不是\(1\),那么\((x-px,y-py)\)也一定被占領,且其四角第一次被經過的輪數最小值減少\(1\)。即所有被占領的領地可以看作是至少一個角在第一輪時就被經過的被占領的正方形沿\((px,py)\)平移若干次經過的所有正方形的並。
先將所有點按序放在若干個集合里,一個集合里的所有點滿足任意一個點都能通過向量\((px,py)\)和其相反向量平移若干次到達其他所有點。對於點對\((x,y)\)可以這樣轉換:當\(px = 0\)或\(py= 0\)的時候用增量為\(0\)的坐標和另一維坐標模它的對應增量構成點對進行分離;否則可以用點對\((x \mod px , x \times py - y \times px)\)進行分離。不難發現兩個點對在同一個集合內當且僅當轉換后點對相同。
接着枚舉所有滿足存在一個點最小經過輪數為1的正方形,算出其四角的輪數最大值,就可以知道它會沿着\((px,py)\)平移多少次。把所有矩形的次數算出來,但是有一些正方形平移之后會和之前的正方形重疊一部分。我們再把所有正方形按照給點分集合的方式分成若干可能會導致重疊的集合,對於每一個集合跑一遍區間覆蓋就好了。
斷層
題目意思是:在一個無限大的平面內,初始每個點的權值是其縱坐標。有\(Q\)次操作,每次操作是把所有滿足\(x+y \geq X_i\)或者\(x-y \leq X_i\)的點沿着直線向上平移\(L_i\),問最后\((0.5,0)(1.5,0)...(N-0.5,0)\)這些點的權值。
把操作過程反過來,也就是最開始有\(N\)個點\((0.5,0)(1.5,0)...(N-0.5,0)\),每次操作把滿足\(x+y \geq X_i\)或者\(x-y \leq X_i\)的點沿着直線下陷\(L_i\),問最后每個點的縱坐標。不難發現在任意一次操作后,所有點橫坐標大小關系不改變、橫縱坐標和大小關系不改變、橫縱坐標差大小關系不改變,也就是說每次下陷的一定是一段前綴或后綴。那么需要數據結構支持前綴減、后綴加、得到第一個滿足權值大於/小於某個給定值的位置,線段樹可以勝任。
JOI2017
焚風現象
維護差分數組。
准高速列車
對每個高速列車站維護當前在這個高速站之后、下個高速站之前第一個到不了的城市和如果這里放一個准高速列車站會使得多少個站點被額外經過,然后貪心,每一次選擇過后更新一下兩個值。
JOIOI王國
整個圖的四個角一定不會同時被同一個省占領。假設JOI省占領了權值最小的點,先枚舉JOI省占領了哪個角落,然后從大到小枚舉JOI省占領的最大的點,每一次枚舉之后把這個點丟到IOI省去,更新IOI省占領的土地。用二分進行更新不難做到\(O(n^2logn)\)。
足球
分析性質,不難發現這些性質:
1、一個人將球傳出去之后再控球一定不優,因為此時傳球變得沒有必要。這意味着我們不需要記錄每個人在任何時刻的狀態,只要記錄每個人有沒有控過球就行了。但這樣似乎仍然很不好做。
2、假想在某個人控球之后,其原來所在的位置仍有一個球員,那么在這個人控球后也不會將球傳給其原來所在的位置的球員,因為可以證明這樣的方案一定不如“在這個人之前控球的人將球傳到這個人所在的位置”優。這樣就算一個人控過球也可以認為原來位置有一個球員。
這樣就完全無需記錄球員相關信息,只需記錄球是否被球員控住。跑bfs求出每個點到最近球員的距離\(dis\),然后對於每個位置建三個點表示球在當前位置,有球員控球/沒球員控球,在往東西方向滾/沒球員控球,在往南北方向滾。從控球到滾的代價是\(B\),從滾到控球的代價是\(dis \times C\),滾和控球往相鄰方向的代價就是\(A\)和\(C\)。然后跑最短路。
繩
JOI2018
寒冬暖爐
題目相當於用\(K\)個區間覆蓋\(N\)個點使得區間總長最小。這等價於先用1個區間覆蓋\(N\)個點,然后挖掉\(K-1\)個兩個相鄰的點之間的區間。挖掉的區間長度越長越好,於是求出相鄰兩個位置的距離然后取前\(K-1\)大。
美術展覽
按照美觀度排序之后一次選擇一定選一段區間。枚舉這個區間的左端點,那么之后每幅畫的貢獻就是“它的權值-(它的美觀度-它前面的畫的美觀度)”,維護最大前綴和即可。
團子制作
有可能互相影響的串會滿足其中的G團子的橫縱坐標之和相同。
枚舉這個和,把所有滿足的G團子拿出來DP:\(f_{i,j}\)表示確定了前\(i\)個團子,第\(i\)個團子的串是橫/縱的最大串數。轉移枚舉第\(i\)個團子的串的方向,那么與其恰好相鄰的G團子的方向必須與它一致。
月票購買
先跑若干遍最短路跑出U到每個點的最短路\(disu_x\)、每個點到\(V\)的最短路\(disv_x\)、\(S\)到\(T\)的最短路DAG,然后在DAG上拓撲求出每個點的拓撲后繼到達\(T\)的最短路\(f_x\),然后將\(disu_x+f_x\)取min就是答案。
毒蛇越獄
一個顯然做法是枚舉?是什么,復雜度\(O(2^{cnt?})\)。只用這種做法太low了,我們考慮拼上一些其他做法。
考慮高維前綴和,不難發現我們可以把?看作\(1\),對原序列跑一邊高維前綴和,然后通過容斥把原序列中的\(1\)算多的部分減掉。這里的復雜度是\(O(2^{cnt1})\)的。
不難發現還可以把所有\(01\)翻轉做高維前綴和,復雜度是\(O(2^{cnt0})\)的。
\(cnt0 , cnt1 , cnt?\)三者的最小值是\(\lfloor \frac{L}{3} \rfloor\),所以預處理兩個高維前綴和可以將復雜度做到\(O(2^LL+2^{\lfloor \frac{L}{3} \rfloor}Q)\)。
JOI2019
勇者比太郎
算一下I和O的前綴和。
畫展
如果確定了畫框的數量,那么畫框一定是從大到小選最優。於是從大到小給每個畫框選擇能夠選的美觀度最大的畫。
有趣的家庭菜園3
DP:設\(f_{i,j,k,0/1/2}\)表示已經確定了序列的前\(i\)個位置、紅色放了\(j\)個、綠色放了\(k\)個、最后一個元素的顏色是紅/綠/黃時的最小步數,因為對於每種顏色,選擇的元素是一段前綴,所以不難算出往后加一個元素時移動的代價。
硬幣收藏
首先把所有的硬幣移到它們在\(x \in [1,N] , y \in [1,2]\)中距離它們最近的位置並計算距離。
然后從左往右考慮列,將硬幣和空位匹配。經過分析發現:若考慮到第\(i\)列,第\(i\)列上有額外硬幣,在之前列上有空位,那么無論硬幣和空位的縱坐標如何,當前硬幣補充到之前空位上不劣。
如果空位和硬幣在同一行顯然補上去更優;否則假設空位由另一行更靠后的硬幣填補,當前硬幣填補本行靠后的空位,可以發現這樣匹配橫坐標方向額外經過的長度至少為\(2\),而在之前的填補方式中橫坐標沒有額外經過長度,縱坐標額外經過的長度為\(2\),所以選擇之前的填補方式不會更劣。
那么按照這樣的方式進行匹配,盡量同行和同行進行匹配,如果不能同行匹配但仍有額外硬幣和空位則跨行匹配,算出貢獻即可。
獨特的城市
把直徑拿出來,設兩個端點為\(S\)和\(T\)。那么對於一個點,如果其離\(S\)更近,則不難證明對它來說獨特的城市只能是它到\(T\)的路徑上的點。
於是分別從\(S,T\)開始dfs,用一個棧維護\(S,T\)到當前點的路徑上合法的獨特點,然后用一個桶記錄一下棧里面每種特產的出現次數。每次向下dfs的時候,先dfs重兒子再dfs輕兒子,每次把棧里面不合法的獨特點從棧里面彈掉,把當前點入棧,然后往下dfs。不難發現在這樣的dfs順序下當一個點從棧里面被彈掉之后,在之后的過程中也一定沒有用了,所以不會有額外的入棧操作,每一個點只會入棧它的兒子那么多次,總入棧次數就是\(O(n)\)的。所以復雜度是\(O(n)\)的。
