WC2019 題目集


最近寫的一些 WC2019 上講的一些題。還是怕忘了,寫點東西記錄一下。

LOJ2983 「WC2019」數樹

題意

本題包含三個問題:

  • 問題 0:已知兩棵 \(n\) 個節點的樹的形態(兩棵樹的節點標號均為 \(1\)\(n\)),其中第一棵樹是紅樹,第二棵樹是藍樹。要給予每個節點一個 \([1, y]\) 中的整數,使得對於任意兩個節點 \(p, q\),如果存在一條路徑 \((a_1 = p, a_2, \cdots , a_m = q)\) 同時屬於這兩棵樹,則 \(p, q\) 必須被給予相同的數。求給予數的方案數。
    • 存在一條路徑同時屬於這兩棵樹的定義見「題目背景」。
  • 問題 1:已知藍樹,對於紅樹的所有 \(n^{n−2}\) 種選擇方案,求問題 0 的答案之和。
  • 問題 2:對於藍樹的所有 \(n^{n−2}\) 種選擇方案,求問題 1 的答案之和。

提示:\(n\) 個節點的樹一共有 \(n^{n−2}\) 種。

在不同的測試點中,你將可能需要回答不同的問題。我們將用 \(\text{op}\) 來指代你需要回答的問題編號(對應上述 0、 1、 2)。

由於答案可能很大,因此你只需要輸出答案對 \(998, 244, 353\) 取模的結果即可。

所有測試點均滿足 \(3 \le n \le 10^5, 1 \le y \lt 998244353, \text{op} \in \{0, 1, 2\}\)

題解

\(\text{op} = 0\) 送分。

op = 1

考場上其實想到了 \(O(n^2)\)\(\text{DP}\) 做法,然而沒有意識到自己容斥容反了,調了一個世紀也沒調出來……耗去了2.5h最后卻只收獲了暴力分……賽后補題的時候,嘗試把考場代碼重新寫一下,結果再次得到了和考場上一樣的答案……最后想了好久才找到問題……

無根樹計數容易想到利用 Prufer 序列。考慮枚舉藍樹上的一個邊集 \(S\) ,強制讓紅樹選擇這個邊集,這樣紅樹就形成了 \(m = n - |S|\) 個聯通塊。我們把每個聯通塊縮成一個點,再將這些點用一些邊相連,就是紅樹的方案數。注意一個點的度數對應到 Prufer 序列上就是這個點的出現次數 + 1。因此,對應到 Prufer 序上,如果我們定義一個 Prufer 序列的權值,等於 Prufer 序列上每個位置代表的聯通塊的大小的乘積,那么紅樹的方案數,就相當於所有 Prufer 序列的權值之和,在乘上每個聯通塊的大小的乘積。由於 Prufer 序上每個位置的出現都是等概率的,所以所有的方案之和,就等於 \(n^{m - 2} \displaystyle \prod_{i = 1}^{m} sz_i\) 。其中 \(sz_i\) 表示第 \(i\) 個聯通塊的大小。

注意到在這里我們只強制了某些樹邊一定存在,但沒有強制其它邊一定不存在,因此還要考慮怎么容斥。考慮對於紅樹和藍樹有 \(i\) 條重邊的時候,對答案的貢獻是 \(y^{n - i}\)。而如果我們按照上面所述進行計算的時候,我們會對 \(S\) 的每個子集算出它的貢獻。也就是說,我們需要構造一個容斥系數,滿足 \(\displaystyle \sum_{i = 0}^{|S|} y^{n - i} {|S| \choose i} f_i = y^{n - |S|}\)

我們考慮在最后對答案乘上 \(y^n\) ,用 \(y\) 的逆元代替 \(y\),則上式可化為 \(\displaystyle \sum_{i = 0}^{|S|} y^i {|S| \choose i} f_i = y^{|S|}\) 。容易發現,\(y^i f_i = (y - 1)^i\)

有了前面這些東西,我們就可以直接 \(\text{DP}\) 了。設 \(dp_{i,j}\) 表示考慮到了 \(i\) 的子樹,\(i\) 所在的聯通塊大小為 \(j\) 的貢獻之和。轉移直接拼子樹,然后在 \(i\) 處考慮是否斷開 \(i\)\(i\) 的父親之間的邊就行了。

考試的時候犯的錯誤,就是少了對 \(y\) 求逆,答案乘上 \(y^n\) 那一步,然后就導致容斥的時候往 \(> |S|\) 的那個方向容斥了,過不了樣例的時候心態很爆炸……缺少冷靜分析。

\(O(n^2)\) 優化到 \(O(n)\) 只需要考慮這個 \(\text{DP}\) 的組合意義。注意到難以處理的地方就在於這個聯通塊大小的乘積。我們發現這個東西的組合意義,就是在每個聯通塊內選出一個點,因此可以用 \(dp_{i,0/1}\) 表示是否在這個聯通塊內選出了點,直接 \(\text{DP}\) 即可。

op = 2

事實上,有了我們剛剛的推論,接下來的問題也就沒那么復雜了。考慮兩棵樹都沒有確定時,同樣枚舉兩樹的公共邊集 \(|S|\),那么接下來只需要把兩棵樹分別連起來就好。因此答案就可以表示為 \(\displaystyle \sum_{S} (n^{m - 2} \displaystyle \prod_{i = 1}^{m} sz_i)^2 (y - 1)^{|S|}\)

考慮對集合 \(S\) 進行 \(\text{DP}\)。設 \(dp_{i}\) 表示選出了一個大小為 \(i\) 的集合的貢獻之和。轉移只需要枚舉一個聯通塊的大小 \(j\),然后把貢獻拆掉乘進去就行了。注意需要強制轉移的順序,因為聯通塊之間是無序的。這樣就可以得到一個 \(O(n^2)\) 的解法了。

很容易發現這個 \(\text{DP}\) 可以輕松用多項式優化。可以寫分治 + \(\text{FFT}\),也可以多項式 \(\text{exp}\) 。復雜度分別為 \(O(n \log^2 n)\)\(O(n \log n)\),通過此題都綽綽有余。

總結

容斥的時候一定要仔細分析清楚啊……至今仍然對這個題耿耿於懷。畢竟在得到 \(op = 1\)\(O(n^2)\) 做法之后,想要得到 \(op = 2\)\(O(n \log^2 n)\) 做法實在難度不大。不過考試畢竟是考試吧,與平常的練習肯定是有區別的。即使當時心態沒有爆炸,在考場上真的寫出了這個題的高分做法的話,其他題的得分也不會太好看。所以說考試的時候還是要適當取舍,要的就是盡快搶分,而不是死剛一題的倔強。

以及自己對於 Matrix-Tree 那套理論不大熟悉,也要注意一下。

LOJ2864 「IOI2018」排座位

題意

你要在一個長方形大廳里舉辦國際編程比賽,該大廳共有 \(HW\) 個座位(\(H\)\(W\) 列)。行的編號是從 \(0\)\(H-1\),列的編號是從 \(0\)\(W-1\)。位於 \(r\)\(c\) 列的座位用 \((r,c)\) 表示。一共邀請了 \(HW\) 位參賽者,編號是從 \(0\)\(HW-1\)。你制定好了一個座位表,第 \(i\)\(0\le i\le HW-1\))個參賽者被安排到座位 \((R_i,C_i)\)。座位表中參賽者和座位是一一對應的。

大廳中一個座位集合 \(S\) 被稱為是長方形的,如果存在整數 \(r_1,r_2,c_1\)\(c_2\) 滿足下列條件:

  • \(0\le r_1\le r_2\le H-1\)
  • \(0\le c_1\le c_2\le W-1\)
  • \(S\) 正好是所有滿足 \(r_1\le r\le r_2\)\(c_1\le c\le c_2\) 的座位 \((r,c)\) 的集合。

如果一個長方形座位集合包含 \(k\)\(1\le k\le HW\))個座位,並且被分配到這個集合的參賽者的編號恰好是從 \(0\)\(k-1\),那么該集合是美妙的。一個座位表的美妙度定義為這個表中美妙的長方形座位集合的個數。

在准備好座位表后,你會收到一些交換兩個參賽者座位的請求。具體來說,有 \(Q\) 個這樣的請求,按時間順序編號為 \(0\)\(Q-1\)。第 \(j\)\(0\le j\le Q-1\))個請求希望交換參賽者 \(A_j\)\(B_j\) 的座位。你立即接受每個請求並更新座位表。每次更新后,你的目標是計算當前座位表的美妙度。

  • \(1\le H\)
  • \(1\le W\)
  • \(HW\le 1\ 000\ 000\)
  • \(0\le R_i\le H - 1\)\(0 \le i \le HW - 1\)
  • \(0\le C_i\le W - 1\)\(0 \le i \le HW - 1\)
  • \((R_i,C_i)\ne(R_j,C_j)\)\(0 \le i < j \le HW-1\)
  • \(1\le Q\le 50\ 000\)

題解

題目中 Subtask5 是個很重要的提示,即考慮對於 \(H = 1\) 的情況該如何處理。注意到當 \(H = 1\) 時,等價於求有多少個 \(k\) 滿足如果我們將所有編號小於等於 \(k\) 的格子染黑,那么這些黑色格子必須要形成一個聯通塊,即 \(點數 - 邊數 = 1\)。我們發現,每次修改只會對常數個位置的點數和邊數進行修改,因此就可以在 \(O(Q \log (HW))\) 的時間內解決 Subtask5。

對於 Subtask6,一維坐標變為了二維坐標,這樣不僅要求只有一個聯通塊,還要求這個聯通塊一定是一個矩形。把規律總結一下,就需要滿足以下兩個條件:

  • 左邊和上面都不是黑點的黑點有恰好 \(1\) 個。
  • 上下左右有至少 \(2\) 個黑點的白點只有 \(0\) 個。

看起來需要滿足兩個條件,然而條件 \(1\) 中合法的不會 \(< 1\) 個,條件 \(2\) 中合法的不會 \(< 0\) 個,於是只要加起來看是否 \(= 1\) 就行了。每次修改同樣只會對常數個位置進行修改,因此直接用線段樹維護這兩個值相加的最小值和最小值個數即可。時間復雜度 \(O(Q \log (HW))\)

總結

對於這類聯通塊個數相關的問題,可以考慮轉化為點數 - 邊數。然后用數據結構來維護相關信息,很常用的一個套路。

LOJ2866 「IOI2018」機械娃娃

題意

告訴你觸發器的數量 \(M\)。再給你一個長度為 \(N\) 的序列 \(A\),序列的每個元素都是某個觸發器的序列號。每個觸發器會在序列 \(A\) 中出現若干次(也可能是零次)。你的任務是設計一個管路,以滿足如下條件:

  • 球在若干步之后返回到起點。
  • 當球首次返回到起點時,所有開關的狀態都是 X
  • 在球首次返回到起點時,此前它進入所有觸發器的總次數恰好為 \(N\)。這些被進入過的觸發器,其序列號按照被球經過的順序依次為 \(A_0,A_1,\ldots ,A_{N-1}\)
  • \(P\) 為球首次返回到起點時,球所引起的所有開關狀態切換的總次數。\(P\) 不能超過 \(2\times 10^7\)

同時,你不想用太多的開關。

對於全部數據,\(1\le M\le 10^5,1\le N\le 2\times 10^5,1\le A_k\le N\ (0\le k\le N-1)\)。如果 \(S\le N+\log_2 N\),你將獲得該測試樣例的滿分。

題解

如果序列長度恰好滿足 \(N = 2^k\) ,那么可以考慮用一個二叉樹進行構造。其中每個節點往下連接兩個節點處,使用一個開關,最后所有的葉子節點連向目標觸發器,每個觸發器連向二叉樹的根,最后一個葉子節點直接連回起點。注意由於最后這個葉子不能用上,所以可以在起點先連向第一個觸發器。

如果 \(N \neq 2^k\) ,可以考慮 \(N\) 的二進制拆分,然后對於每個二進制為 \(1\) 的位都建一棵二叉樹相連。這樣總開關個數就是 \(N + \log_2 N\) 級別了。

總結

純粹的腦洞題吧,不過還是可以從交互次數出發,思考一下相關的做法。

LOJ2867 「IOI2018」高速公路收費

題意

在日本,城市是用一個高速公路網絡連接起來的。這個網絡包含 \(N\) 個城市和 \(M\) 條高速公路。每條高速公路都連接着兩個不同的城市。不會有兩條高速公路連接相同的兩個城市。城市的編號是從 \(0\)\(N-1\),高速公路的編號則是從 \(0\)\(M-1\)。每條高速公路都可以雙向行駛。你可以從任何一個城市出發,通過這些高速公路到達其他任何一個城市。

使用每條高速公路都要收費。每條高速公路的收費都會取決於它的交通狀況。交通狀況或者為順暢,或者為繁忙。當一條高速公路的交通狀況為順暢時,費用為 \(A\) 日元(日本貨幣),而當交通狀況為繁忙時,費用為 \(B\) 日元。這里必有 \(A\lt B\)。注意,\(A\)\(B\) 的值對你是已知的。

你有一部機器,當給定所有高速公路的交通狀況后,它就能計算出在給定的交通狀況下,在兩個城市 \(S\)\(T\)\(S\neq T\))之間旅行所需要的最小的高速總費用。

然而,這台機器只是一個原型。所以 \(S\)\(T\) 的值是固定的(即它已經被硬編碼到機器中),但是你並不知道它們的值是什么。你的任務就是去找出 \(S\)\(T\) 的值。為了找出答案,你打算先給機器設定幾種交通狀況,然后利用它輸出的高速費用來推斷出 \(S\)\(T\)。由於設定高速公路交通狀況的代價很大,所以你並不想使用這台機器很多次。

對於全部數據:

  • \(2\le N\le 9\times 10^4,1\le M\le 1.3\times 10^5,1\le A\lt B\le 10^9\)
  • 對於每一個 \(0\le i\le M-1\)
    • \(0\le U[i],V[i]\le N-1\)
    • \(U[i]\neq V[i]\)
  • 保證數據無重邊。
  • 你可以從任何一個城市出發,通過高速公路到達其他任何一個城市。
  • \(0\le S,T\le N-1,S\neq T\)

設函數 ask 調用了 \(X\) 次。如果 \(X\le 50\) ,你將獲得滿分。

題解

首先考慮樹的情況怎么做。考慮從任意一個點開始,在 bfs 序上二分,可以輕松找到那個 bfs 序更大的那個點。再從這個點出發,再做一次 bfs 二分就能得到起點與終點的位置。次數是 \(2 \log_2 N\) 的。

當我們把樹上的做法擴展到圖上的時候,我們發現二分並不能直接得到 bfs 更大的那個點了。但我們可以任選一個點作為起點,建出它的 bfs 樹,用二分得到最短路上的一個點。我們再以這個點為根,建立 bfs 樹。容易發現這樣我們就轉化為了樹的情況。不過這么做,我們需要 \(3 \log_2 N + 1 = 52\) 次。

考慮沿用上面的方法,但是我們這次直接二分得到最短路上的一條邊。這樣我們對這條邊的兩側分別做一次二分,這樣由於兩側大小除了個 \(2\) 。於是次數就變成了 \(3 \log_2 \frac{N}{2} + 1 = 50\) 次,出題人早幫你算好了。

總結

對於圖上的某些問題,可以先考慮將問題特殊化的做法——即放到樹上考慮。以及同樣的,可以對着交互次數來湊算法。

LOJ2868 「IOI2018」會議

題意

\(N\) 座山橫着排成一行,從左到右編號為從 \(0\)\(N-1\)。山 \(i\) 的高度為 \(H_i\)\(0\le i\le N-1\))。每座山的頂上恰好住着一個人。

你打算舉行 \(Q\) 個會議,編號為從 \(0\)\(Q-1\)。會議 \(j\)\(0\le j\le Q-1\))的參加者為住在從山 \(L_j\) 到山 \(R_j\)(包括 \(L_j\)\(R_j\))上的人(\(0\le L_j\le R_j\le N-1\))。對於該會議,你必須選擇某個山 \(x\) 做為會議舉辦地(\(L_j\le x\le R_j\))。舉辦該會議的成本與你的選擇有關,其計算方式如下:

  • 來自每座山 \(y\)\(L_j\le y\le R_j\))的參會者的成本,等於在山 \(x\)\(y\) 之間(包含 \(x\)\(y\))的所有山的最大高度。特別地,來自山 \(x\) 的參會者的成本是 \(H_x\),也就是山 \(x\) 的高度。
  • 會議的成本等於其所有參會者的成本之和。

你想要用最低的成本來舉辦每個會議。

注意,所有的參會者將在每次會議后回到他們自己的山;所以一個會議的成本不會受到先前會議的影響。

對於全部數據:

  • \(1\le N,Q\le 7.5\times 10^5\)
  • \(1\le H_i\le 10^9\) \((0\le i\le N-1)\)
  • \(0\le L_j\le R_j\le N-1\) 且保證區間兩兩不同(\(0\le j\le Q-1\))。

題解

考慮最后選擇的會議點 \(p\) 會在哪里。如果恰好選在了區間最大值處,可以直接計算,否則一定處在最大值某一側。兩側顯然是等價的,不妨設這個點位於區間最大值的右側。

我們發現,如果這個點 \(p\) 位於區間 \([l,r]\) 最大值 \(x\) 的右側,那么這個貢獻可以轉化為一個 \([x + 1,r]\) 的子問題加上一個定值。於是可以考慮每次找到區間最大值,遞歸兩側來解決,即建出序列的笛卡爾樹。可以發現,我們只要能對於笛卡爾樹上的每一個區間 \([l,r]\) 維護出所有區間 \([l,i](l \le i \le r)\) 的最小成本,那么最后的答案也能輕松計算了。顯然對於區間 \([l,r]\) 的最大值的位置 \(x\)\(l \le i \le x\) 的最小成本我們可以直接從左子樹沿用過來,所以我們只需要快速維護處右子樹的答案。可以發現

\[Cost(l,i) = \min(Cost(l,x − 1)+(i − x + 1) \times H_x , Cost(x + 1,i) + (x − l + 1) \times H_x) \]

注意到這里兩個決策是有可二分的,即從某個位置 \(pos\) 開始,后面的決策會更優,否則前面的決策會更優。這是因為 \(Cost(x + 1, i + 1) − Cost(x + 1, i) \le \max_{x + 1 \le j \le i + 1}H_j \le H_x\)

於是我們可以直接用線段樹來維護這些最小成本。在線段樹上二分得到 \(pos\) ,隨后只要支持區間加等差數列,區間賦值即可。時間復雜度 \(O((n + q) \log n)\)

總結

題目很神仙,很多地方思路挺卡人的。包括第一步里面考慮決策點與最大值的位置關系,發現信息可以共用之后,建出笛卡爾樹來計算,以及最后這個決策可二分都比較巧妙。

對於這種貢獻和區間最大值關系密切的問題,建出笛卡爾樹可以讓題目更加清晰。以及對於不少問題,利用樹的結構來共用信息,降低復雜度——這似乎是一個很通用的 idea,本質上有點類似於記憶化搜索?

LOJ2390 「JOISC2017 Day 1」開荒者

題意

開荒者要讓一片長方形的平原長滿草。這個長方形可視為 \(W\)\(H\) 列的正方形網格,底平行於南北方向,高平行於東西方向。我們用 \((1,1)\) 表示網格的西北角,\((W,H)\) 表示網格的東南角。開始時有且只有 \(N\) 個網格內長有野草,其余網格內既沒有草也沒有草籽。開荒者們可以控制這片平原上的風往東、西、南、北四個方向中的一個方向吹。

每年夏天,草會制造草籽。開荒者們會選定當年夏天的風向。所有草籽會被風揚起,並根據風向移動一格。來年春天,有草籽降落的網格就會有草萌發。假設草不會枯萎。

我們假設不會有草籽從平原外飄進平原內,飄出網格的草籽不會發芽。試求:讓這片平原長滿草最少需要幾年。

\(1\le N\le 300, 1\le W, H\le 10^9, 1\le S_i \le W, 1\le E_i\le H, (S_i, E_i)≠(S_j, E_j)(1\le i<j\le N)\)

題解

考慮只有一行的情況,設最左邊的草與左邊界的距離為 \(A\) ,最右邊的草與右邊界的距離為 \(B\) ,相鄰兩個長有草的格子最大距離為 \(C\) 。這樣最小步數就是 \(\max\{A + B,C\}\)

注意到操作順序是無關的,所以先枚舉往上走了 \(X\) 步,往下走了 \(Y\) 步。注意到這里可以看做枚舉一共向下走了 \(X + Y\) 步,再把網格往下移動 \(Y\) 步。這樣我們用一個單調隊列維護 \(\max A,\max B,\max C\) 即可在 \(O(NH)\) 內解決。

顯然有效狀態數不多,考慮如何壓狀態。首先考慮,如果我們確定了 \(X + Y\) 的值,即形成了一列一列的草塊,哪些位置作為網格的上邊界是有效的。我們只需要保留那些上邊界從上往下掃過來,一行的狀態變優的位置。這些位置就只包含第一行,以及所有草塊開頭的行。這樣合法的 \(X + Y\)\(O(n)\) 級別的。

現在如果我們確定了網格的位置,考慮哪些 \(X + Y\) 是有效的。

  • 首先很顯然所有的 \(x_j < x_i,X + Y = x_i - x_j - 1\) 是有效的。可能會有疑問,為何 \(X + Y = x_i - x_j\) 不是有效的呢?如果 \(X + Y = x_i - x_j\) 意味着第 \(i\) 塊草與第 \(j\) 塊草同時存在於某一行。對於 \(H \ge 2\) 的情況,與這一行相鄰的兩行相對於這兩個元素的狀態,仍然是相同的。因此這種操作沒有意義;對於 \(H = 1\) 就不存在這種情況。
  • 還有一個有些容易忽略的就是,\(X + Y = x_i - x_j + H - 1\) ,這個原因需要結合之前所說的合法網格上邊界。考慮一個合法的上邊界為 \(x_i\) ,這樣當 \(X + Y\)\(0\) 開始不斷增大的過程中,當 \(X + Y = x_i - x_j + H - 1\) 的時候,\(x_j\) 處的草會長到下邊界處。這樣對於 \(x_j\) 而言狀態發生了改變,因此需要記錄下來。

於是合法的 \(X + Y\) 的數量就是 \(O(n^2)\) 級別的。利用精致的枚舉我們終於把這個題優化到了 \(O(n^3)\)

總結

又是一道神仙題,而且還比較難寫……對於狀態數十分稀疏的問題,可以考慮哪些狀態是有效的,然后對於有效的狀態去枚舉或者 DP。尤其是對於一類最優化問題,需要仔細分析各種性質,盡可能舍棄重復或者不優於某些狀態的狀態。

LOJ2392 「JOISC 2017 Day 1」煙花棒

題意

\(N\) 人站在一條數軸上。他們人手一個煙花,每人手中的煙花都恰好能燃燒 \(T\) 秒。每個煙花只能被點燃一次。

\(1\) 號站在原點,\(i\)\((1\le i\le N)\)\(1\) 號的距離為 \(X_i\)。保證 \(X_1=0,\) \(X_1, X_2, \dots, X_N\) 單調遞增(可能有人位置重疊)。

開始時, \(K\) 號的煙花剛開始燃燒,其他人的煙花均未點燃。他們的點火工具壞了,只能用燃着的煙花將未點燃的煙花點燃。當兩人位置重疊且其中一人手中的煙花燃着時,另一人手中的煙花就可以被點燃。忽略點火所需時間。

求至少需要以多快的速度跑,才能點燃所有人的煙花(此時可能有些人的煙花已經熄滅了)。速度必須是一個非負整數

\(1\le K, N \le 10^5, 1\le T\le 10^9, 0\le X_i\le 10^9 (1\le i\le N), X_1 = 0, \{X_N\}\) 單調遞增。

題解

顯然需要二分答案。注意到無論 \(K\) 號怎么走,其他的人肯定都是向中間靠攏。我們讓 \(K\) 不動,相當於可以讓左側的人或者右側的人以 \(2s\) 的速度向 \(K\) 靠攏。我們求出 \(K\) 左側和 \(K\) 右側的相鄰兩人距離。這樣,模型轉化為,有兩個棧,棧中每個元素有一個權值,你不能在任何時刻使得你取的每個元素權值之和為負數,問是否可能。

類似於 HNOI2018 Day2T2 的貪心,就能得到一個 \(O(n \log^2 n)\) 的做法。不過這里我只需要判斷是否合法,相較於原問題少了一個限制。事實上這種做法還不夠優秀。

考慮如果取走某個棧的某個前綴之后,會使得你手上的權值之和變大,並且你手上的權值可以保證在拿這個前綴的過程中不會變成負數,那么你肯定會貪心取。這么不斷取下去,直到不能取為止。如果此時存在某個棧的某個前綴和為正,但是卻取不到,那么一定是無解的。

剩下的該如何做呢?可以發現如果把取物品的過程倒過來,從序列末尾開始做一次相同的貪心。即初始手上的權值即為兩個序列的權值之和,每次取一個物品,就將手上的權值減去物品的權值。如果最后也能貪心取到之前兩個序列的位置,那么就是合法的。不太好解釋到底怎么想到的,感覺這個地方不是那么好想。

總結

對於模型比較繞的題目,可以先轉到一個十分基礎的模型再來考慮問題。感覺 HNOI2018 Day2T2 那種貪心運用還挺廣泛的。

LOJ2398 「JOISC2017 Day 3」自然公園

題意

JOI 島是一個觀光勝地,全島被定為一個自然公園。

JOI 島有 \(N\) 個廣場和若干條道路。廣場從 \(0\)\(N - 1\) 編號。每條道路聯結島內兩個不同的廣場,可以雙向通行。對於每一個廣場,至多有 \(7\) 條道路將它與其他廣場相連。對於任意兩個不同的廣場,至多有 \(1\) 條道路將它們相連。此外,我們已知任意兩個廣場都可以通過若干條道路互相到達。

你和你的朋友 IOI 醬決定考察 JOI 島。為了考察能夠高效進行,你不得不掌握全島的結構。島上的諸多動物會帶來危險,因此由運動細胞發達的 IOI 醬去探索全島,而你則負責基於 IOI 醬的報告來確定島的結構。

你可以對 IOI 醬指定兩個廣場 \(A\)\(B\),以及若干可以經過的廣場,向其詢問是否可以只經由指定的廣場從 \(A\) 到達 \(B\)。IOI 醬會按照詢問的內容在島上探索並報告結果。

由於考察不能持續過長時間,需要將詢問次數限制在 \(45\,000\) 次以內。

題解

先考慮樹怎么做。考慮現在有一棵已知樹,現在需要擴展一個節點。首先可以用一次二分得到編號最小的與已知樹相鄰的節點。接着在 bfs 序上二分就可以得到這個節點和已知樹中哪個點相連。這樣總次數 \(2n \log_2 n\)

擴展到圖上之后,我們還是可以在 \(\log n\) 的時間找到一個與已知圖相鄰的點 \(x\) 。隨后我們還要找到和 \(x\) 直接相鄰的剩下的點,這里我就要刪掉已經找到的點。對於每個形成的聯通塊,先判斷這個聯通塊內是否有與 \(x\) 相連的點,隨后繼續二分。容易發現這樣的次數是 \(O(7m + (n + m) \log n)\) 的。

總結

仍然是從特殊問題出發;對於這種全圖的狀態未知,需要通過詢問得到的問題,可以考慮按點或者按邊依次考慮,不斷加入已知圖。

Codeforces Gym101955F Counting Sheep in Ami Dongsuo

題意

有一張 \(n\) 個點的拓撲圖,每個點有個權值。權值不超過 \(w\)。有三個人要從同一個點出發,走三條不同的路線,這種方案的權值就是他們最后停在的那三個點的權值和。對於 \(k = 0 \sim 3w\) ,問權值為 \(k\) 的方案有多少種。模 \(10^9 + 7\)

\(n \le 10000,m \le 30000,w \le 400\)

題解

三個人路線不同隨便容斥一下就行了。接下來直接考慮從某個點出發,三個人結束時點權和為 \(w\) 的方案數。直接 \(\text{FFT}\) 合並可以做到 \(O(mw \log w)\) ,不過不夠優秀。

注意到這里只要求所有點的方案之和,可以考慮先求出每個點的多項式在 \(1 \sim 3w\) 處的點值,直接對點值 \(\text{DP}\) ,最后把多項式再插值插回來。這樣復雜度就優化到了 \(O(mw + w^2)\)

不大會插值插多項式比較好的方法,也看不太懂 wxh 的代碼……就寫了個炒雞麻煩的拉格朗日插值。

總結

比較有意思的 idea。本質上其實也就是對每個多項式 \(\text{DFT}\) 之后再加起來 \(\text{IDFT}\) 。但是由於初始多項式很稀疏,所以求點值就會很快。

Codeforces Gym101981C Cherry and Chocolate

題意

\(A\)\(B\) 在一棵樹上玩游戲。\(A\) 先選一個點染成粉色,\(B\) 選一個點染成棕色,\(A\) 再選一個點染成粉色。然后統計這樣的點 \(v\) 的數量:不存在它到棕色點的不經過粉色點的路徑。

\(A\) 想要最大化這個數量,\(B\) 想要最小化,問最后這個值是多少。

\(n \le 10^5\)

題解

考慮兩個人的策略。首先 \(A\) 選了一個點之后,整棵樹會斷成若干個聯通塊。\(B\) 肯定會選擇若干個聯通塊中的某一個重心。隨后 \(A\) 會選擇這個重心的最大子樹。這樣只要枚舉 \(A\) 選擇的點,就可以做到 \(O(n^2)\)

注意到 \(A,B\) 各選定一個點 \(x,y\) 之后,假如我們現在需要調整 \(A\) 第一步的策略。如果點 \(x\) 向點 \(y\) 相反方向移動的話,那么 \(B\) 保持原有策略,答案不會變得更劣。因此 \(x\) 一定會向着 \(y\) 所在的子樹移動。這樣只要類似於點分治,每次找到 \(y\) 所在的子樹的重心,然后不斷遞歸,復雜度就是 \(O(n \log n)\) 的了。

總結

對於博弈題,一個重要的思考角度就是,分析出讓狀態不會變優的策略,然后減掉這些策略的情況,也許復雜度就會變得正確。

Codeforces Gym101981L Lagrange the Chef

題意

給出 \(n, X, Y\)\(n\) 個數 \(A_i\) ,要通過若干次把某個數移到某個位置的操作,使得不存在相鄰的 \(X\)\(Y\)。問最小操作步數。

\(n \le 5000,\text{TL = 3s}\)

題解

先判掉各種答案是 \(0 / -1\) 的情況。

如果存在一對相鄰的 \(X\)\(Y\) ,只有兩種方式使得他們隔開。一個是往這兩個數中間丟一個不是 \(X\) 或者 \(Y\) 的數;另一個就是把一個 \(X\) 或者 \(Y\) 丟到某個地方去。注意把 \(X\) 到丟到別的地方,要么是丟到一個 \(X\) 旁邊,要么是丟到既不是 \(X\) 也不是 \(Y\) 的旁邊。如果是后者,那么答案至少是 \(X\) 的個數,至多是 \(X\) 的個數 \(+1\) ,這個可以直接判掉。如果是前者,我們就可以在 \(\text{DP}\) 的時候多記一個狀態,即是否有還沒動過的 \(X\)\(Y\) ,最后強制一定有動過的 \(X\)\(Y\)

於是就可以 \(\text{DP}\) 了。設 \(dp_{i,j,x,y,p}\) 表示到了第 \(i\) 個位置,前面空出來了 \(j\) 個不是 \(X\) 也不是 \(Y\) 的數還沒放到序列里,是否有 \(x / y\) 沒有動過,以及上一個的數字是什么,轉移直接對着兩個操作轉移即可,時間復雜度 \(O(n^2)\)

總結

可以考慮將有用的操作進行歸類,總結出每一類的規律,分類進行轉移。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM