1. 程式人生 > 實用技巧 >雜題20201201

雜題20201201

嘗試把之前寫的東西丟上來……


AGC018B Sports Festival

\(N\) 個人,每個人需要恰好參加 \(M\) 個專案之一,你可以決定每個專案的舉辦與否

給定 \(N\times M\) 的矩陣 \(A_{i,\ j}\) ,保證 \(A_{i,\ 1},\ A_{i,\ 2},\ \cdots,\ A_{i,\ M}\) 是一個 \(1,\ 2,\ \cdots,\ M\) 的排列。若專案 \(A_{i,\ p}\)\(A_{i,\ q}\) 都要舉辦,且 \(p<q\) ,那麼第 \(i\) 個人會選擇參加專案 \(p\) 而不是專案 \(q\)

找到一種專案舉辦的方案,使得參加人數最多的專案參加人數最少

\(N,\ M\leq300\)

先假設所有專案都舉辦了,假設參加人數最多的專案 \(p\)\(k\) 個人參加

那麼答案如果要小於 \(k\) ,專案 \(p\) 必定不能舉辦,遞迴下去做即可


AGC018C Coins

\(N=X+Y+Z\) 個三元組 \((A_i,\ B_i,\ C_i)\) ,將其分成大小分別為 \(X,\ Y,\ Z\) 的三組 \(S_1,\ S_2,\ S_3\) ,最大化 \(\displaystyle\sum_{i\in S_1}A_i+\sum_{i\in S_2}B_i+\sum_{i\in S_3}C_i\)

\(N\leq10^5\)

先考慮 \(Z=0\)

時的做法,直接按照 \(A_i-B_i\) 排序即可,前 \(X\) 大的元素即為 \(S_1\) 中的元素

考慮先按照 \(A_i-B_i\) 降序排序,列舉 \(S_1\) 中排名最大的元素的下標 \(x\) ,那麼前 \(p\) 個元素中我們會選恰好 \(X\) 個作為 \(S_1\) ,其餘 \(p-X\) 個元素丟到 \(S_3\) ;後 \(N-p\) 個元素中我們會選恰好 \(Y\) 個作為 \(S_2\) ,其餘 \(N-p-Y\) 個元素丟到 \(S_3\)

對於前 \(p\) 個元素,我們將其按照 \(A_i-C_i\) 排序,選前 \(X\) 個作為 \(S_1\) ,剩下的丟到 \(S_3\)

對於後 \(N-p\) 個元素,我們將其按照 \(B_i-C_i\) 排序,選前 \(Y\) 個作為 \(S_2\) ,剩下的丟到 \(S_3\)

用資料結構維護一下即可,時間複雜度 \(O(n\log n)\)


一道題

給定一張 \(n\) 個點 \(m\) 條邊的有向圖,每條邊有長度和 '('')' 之一的字元

\(q\) 次詢問,每次給定 \(S,\ T\) ,問 \(S\)\(T\) 的一條滿足依次經過的邊上的字元連線起來形成了一個合法括號串的最短路或是判斷無解

  • \(n\leq400\)
  • \(m\leq5\times10^4\)
  • \(q\leq10^5\)

yijan 友情提供了一份題解

我們考慮如何算出 \((u \to v)\) 的合法最短路。由於邊權是非負的,可以用類 dijkstra 求解。

具體來說,考慮 \((u \to v,w)\) 在堆裡面,表示 \(u \to v\) 有合法路徑長為 \(w\) 。然後更新有幾種:

- 可以通過往 \(u \to v\) 後面拼一條路徑更新。也就是 \(u \to v, v \to k\) 兩個合法路徑合併,仍然是合法的路徑,或者可以往前面拼一個路徑更新,也就是 \(k \to u , u \to v\) 這樣。這兩種分別只需要列舉一下 \(k\) 更新即可。這是一個類似 Floyd 的轉移方法,由於 dijkstra 取出的一定是當前的最短路,所以這樣轉移是對的。
- 可以通過 \(p \to u \to v \to q\) 轉移,其中 \(p \to u\) 有一個 ( 的邊,\(v \to q\) 有一個 ) 的邊。也就是往當前的合法括號序兩側加上一對括號。轉移就是列舉一下這兩個邊即可。

場上就寫的這個東西。考慮複雜度分析。點的個數是 \(O(n^2)\) 的,但是邊的個數很多。邊的個數大概是 \(O(m^2 + n^3)\) 的,第一種情況對於每個路徑都有 \(O(n)\) 個轉移,第二種情況,考慮每一對 () 邊,不難發現它們一定能且僅能被轉移一次,所以複雜度是 \(O((m^2 +n^3) \log (m^2 + n^3))\) 的。

看起來很慢,但是會發現實際上 \(m^2\)\(\frac 1 4\) 的常數。所以實際上可以跑過 \(80\) 分。

考慮如何去優化這個東西。

首先,第二種情況中同時列舉 \(p,q\) 非常不優秀。可以發現這個東西可以類似很多狀壓題的優化。也就是設一個 \(g(u,v)\) 表示 \(u \to v\) 的最短路徑,滿足整個路徑開頭是左括號,且匹配完後剩下的一定是開頭的左括號。

發現轉移就稍微有所改變,對於每個 \((u\to v,w)\) 我們列舉一個 \(p \to u\) 滿足這條邊上是一個左括號,然後轉移到 \(g(p,v)\) ,並且把 \(g\) 同樣加入優先佇列中。如果隊首是 \(g\) ,再用 \(g(u,v)\) 轉移到 \((u\to q)\) 。兩次轉移都只需要列舉一次出邊即可。於是邊的個數被優化到了 \(O(nm + n^3)\) ,因為對於一個 \(u\) 列舉所有的 \(v\) 都需要考慮一次所有的 \(u\) 的入邊。

但是這樣的複雜度仍然是 \(O((nm + n^3)\log(nm + n^3))\) ,還是不太能過。

考慮一個經典的 dijkstra 優化。可以通過斐波那契堆來優化。斐波那契堆是一種支援 \(O(1)\) 進行插入,查最大值,更改某個東西的值,合併,且 \(O(\log n)\) 刪除最大值的資料結構。

如果我們提前把 \(O(n^2)\) 個點給插入好,然後每次 push 操作都直接用修改來實現。不難發現這樣佇列裡的元素不會超過開始的點數 ,於是刪除最大值只需要進行 \(O(n^2)\) 次。換句話說,如果圖的點數是 \(O(n)\) 邊數 \(O(m)\) ,那麼斐波那契堆來優化 dijkstra 後複雜度就是 \(O(n\log n + m)\)

於是最後複雜度就變成了 \(O(nm + n^3 + n^2\log(n^2))\) 。可以通過。

考慮一種更陽間的優化方法(雖然貌似有點卡常)。我們可以做值域分塊。這樣就可以做到每次修改最小值 \(O(1)\) ,刪除一個值 \(O(\sqrt{n^2})\)

這樣做複雜度是 \(O(nm + n^3)\) ,常數很大。


CF316D3

有一個初始時 \(p_i=i\)\(n\) 的排列,你每次能交換排列中的兩個位置上的數,同時每個位置有一個交換次數上限 \(a_i\) ,問能夠得到多少種不同的排列,輸出答案 \(\bmod10^9+7\) 的值

\(n\leq10^6;\ a_i=1,\ 2\)

對於一次交換操作 \((i,\ j)\) ,相當於合併兩個環 / 將一個環拆成兩份

考慮如何判斷能否得到一個給定的排列,其合法當且僅當每個環中交換次數上限為 \(1\) 的點數不超過 \(2\)


CF477D Dreamoon and Binary

有一個給定的二進位制數 \(x\) 和一個初值為 \(0\) 的二進位制數 \(n\)

你每次可以進行以下兩種操作之一:

  1. \(n\) 以二進位制從高位往低位輸出,不包含前導 \(0\)
  2. \(n\)\(1\)

你想要使得最終你的輸出結果恰好時 \(x\) 從高位往低位的二進位制表示,求出你最少需要多少次操作以及在保證操作次數最小的前提下有多少種不同的方案有多少種不同的方案(不要求最短)。答案 \(\bmod10^9+7\) 輸出

\(1\leq x<2^{5000}\)

\(g_{i,\ j}\) 為考慮了 \(x\) 的前 \(i\) 位,這一步輸出了 \(x\)\([j,\ i]\) 區間的取值,至少需要多少次操作 \(1\)

\(f_{i,\ j}\) 為考慮了 \(x\) 的前 \(i\) 位,這一步輸出了 \(x\)\([j,\ i]\) 區間的取值,最小化操作 \(1\) 次數的前提下的方案數

統計答案時考慮 \(f_{n,\ i}\) 的貢獻,確定了 \(i\) 的取值後,操作 \(2\) 的次數是固定的,我們只需要判斷操作 \(2\) 的次數 \(+g_{n,\ i}\) 能否更新答案的最小操作次數。注意到 \(5000<2^{13}\) ,我們特殊處理一下 \(n-i\leq13\) 部分即可

有轉移 \(f_{i,\ j}=\displaystyle\sum_{[P(k)=1]}f_{j-1,\ k}\) ,其中 \(P(k)\)\(1\) 當且僅當: \(x\) 在區間 \([k,\ j-1]\) 的取值沒有前導 \(0\) 且不大於在區間 \([j,\ i]\) 的取值; \(k\) 是滿足上述條件的所有位置中 \(g_{j-1,\ k}\) 最小的之一

好像讀錯題了,但本質相同

然後大力字首和一下即可,可能空間有點緊


CF468D Tree

給定一棵 \(n\) 個點的帶邊權樹,求一個字典序最小的排列滿足 \(\displaystyle\sum_{i=1}^ndis(i,\ p_i)\) 最大

\(1\leq n,\ w\leq10^5\)

對於一條邊 \((u,\ v,\ w)\) ,它對答案的貢獻次數的上界是 將這條邊刪掉後形成的兩棵子樹的大小的最小值

一種考慮方式是 \(dis(i,\ p_i)=dep(i)+dep(p_i)-2\times dep(lca(i,\ p_i))\)\(dep_i\)\(i\) 到某個根的邊權和),而 \(dep(i),\ dep(p_i)\) 之和是確定的,我們只需要最小化 \(dep(lca(i,\ p_i))\) 。而取重心為根,根的任意一個兒子子樹大小不超過 \(\frac n2\) ,因此必然存在一個排列,使得 \(lca(i,\ p_i)=rt\)

這樣每條邊的貢獻都達到了上界

我們給 \(rt\) 的每個子樹中的點標記一種顏色, \(rt\) 單獨標記一種顏色

我們將找字典序最小的排列,看作一個二分圖匹配,左部點是 \(i\) ,右部點是 \(p_i\) ,而一個左部點會向所有與其顏色不同的右部點連邊

大概用 Hull 定理搞一下可以得到,這個匹配有解當且僅當 \(2\times max\leq|S|\) ,其中 \(max\) 是左部點中最大的滿足顏色相同的點集大小, \(|S|\) 是右部點剩餘點的數量

接下來我們要按下標升序填 \(p_i\) ,每次填完後需要判斷一下是否合法,用資料結構維護一下即可,有較為優秀的實現方法


CF1438E Yurii Can Do Everything

給定一個長為 \(n\) 的序列 \(a_i\) ,求有多少個數對 \((l,\ r)\) 滿足:

  • \(l+1\leq r-1\)
  • \(a_l\oplus a_r=a_{l+1}+a_{l+2}+\cdots a_{r-1}\)

\(n\leq2\times10^5;\ a_i\in[1,\ 2^{30})\)

我們記 \(s_k=\displaystyle\sum_{i=1}^ka_i\)

那麼合法序列有 \(s_{r-1}-s_l=a_l\oplus a_r\leq a_l+a_r\)

結論:滿足 \(s_{r-1}-s_l\leq a_l+a_r\) 的數對數不超過 \(O(n\log a_i)\)

大概是對於一個 \(l\) ,從左往右考慮每當出現了一個合法的 \(r\) ,字首和大概會翻倍的樣子

一種卡滿的構造是,每三十個數放一組 \(1,\ 2,\ 4,\ 8\cdots,\ 2^{29}\) ,每一組組內會有 \(O(30^2)\) 次貢獻

然後暴力列舉判斷即可


CF452F Permutation

給定一個 \(n\) 的排列,問是否存在一個長為 \(3\) 的子序列是等差數列

\(n\leq3\times10^5\)

假設該下標三元組為 \((i,\ j,\ k)\) ,它們滿足 \(i<j<k\ \and\ p_i-p_j=p_j-p_k=d\)

我們列舉這個 \(d\) ,有一個結論是如果答案存在則有一組三元組的 \(d\) 不大,可以 \(O(nd)\) 解決