Day1
數對
給定 \(n\) 個正整數 \(a_i\),請你求出有多少個數對 \((i, j)\) 滿足 \(1 \le i \le n\),\(1 \le j \le n\),\(i \ne j\) 且 \(a_i\) 是 \(a_j\) 的倍數。
\(2 \le n \le 2\times 10^5\),\(1 \le a_i \le 5 \times 10^5\)。
考慮使用桶,記 \(cnt_x\) 表示 \(a_i = x\) 的個數。
那么分兩種情況討論,第一種情況是 \(a_i = a_j = x\),對答案的貢獻為 \(\sum \binom{cnt_x}{2}\);第二種是 \(a_i = x < a_j = y\),那么這時候枚舉 \(a_i\) 的值 \(x\),同時枚舉 \(x\) 的倍數 \(y\),對答案的貢獻為 \(\sum\limits_{x} \sum\limits_{y} cnt_x cnt_y\)。
時間復雜度是經典的調和級數 \(O(\frac{n}{1} + \frac{n}{2} + \frac{n}{3} + \cdots) = O(n \ln n)\)。
卡牌游戲
有 \(n\) 張卡牌,第 \(i\) 張卡牌的正面有數字 \(a_i\),背面有數字 \(b_i\),初始時所有卡牌正面朝上。
現在可以將不超過 \(m\) 張卡牌翻面,最小化朝上的 \(n\) 個數字的極差。
\(3 \le n \le 10^6\),\(1 \le m < n\),\(1 \le a_i, b_i \le 10^9\),\(a_i\) 單調不下降,\(2n\) 個數字互不相同。
先把 \(a, b\) 拼成一個長度為 \(2n\) 的數組 \(c\) 排序。
對於這個有序的數組,極差即是選中的那一段 \(l, r\) 的 \(c_r-c_l\)。
於是用雙指針枚舉 \(l, r\),不在 \(l, r\) 當中則意味着那一位被刪除,具體要求是:
- 刪掉的 \(a\) 面不超過 \(m\);
- 不存在同一張卡牌的 \(a, b\) 面同時被刪除。
對於第一條開一個 \(cnt\) 維護,第二條開一個 \(vis\) 數組維護即可。
時間復雜度 \(O(n \log n)\),不明白 \(a\) 有序有什么意義。
圖函數
對於一張 \(n\) 個點 \(m\) 條邊的有向圖 \(G\),定義函數 \(f(u, G)\):
- 初始化返回值 \(cnt = 0\),圖 \(G' = G\)。
- 從 \(1\) 至 \(n\) 按順序枚舉頂點 \(v\),如果當前的圖 \(G'\) 中,從 \(u\) 到 \(v\) 與從 \(v\) 到 \(u\) 的路徑都存在,則將 \(cnt + 1\),並在圖 \(G'\) 中刪去頂點 \(v\) 以及與它相關的邊。
- 第 \(2\) 步結束后,返回值 \(cnt\) 即為函數值。
現在給定一張有向圖 \(G\),請你求出 \(h(G) = f(1, G) + f(2, G) + \cdots + f(n, G)\) 的值。
更進一步地,記刪除(按輸入順序給出的)第 \(1\) 到 \(i\) 條邊后的圖為 \(G_i\),請你求出所有 \(h(G_i)\) 的值。
\(2 \le n \le 10^3\),\(1 \le m \le 2 \times 10^5\),\(1 \le x_i, y_i \le n\),沒有重邊和自環。
因為只要求求出每個 \(h(G_i)\),而不是求具體的 \(f(u, G_i)\),所以考慮貢獻法。
考慮一對 \((u, v)\) 如果能對 \(f(u, G_i)\) 造成貢獻,當且僅當存在一條 \(u \to v \to u\) 的環,且這個環上沒有編號 \(<v\) 的點。
於是問題轉化為對於 \(\forall G_i\),求 \(G_i\) 中有多少 \((u, v)\)(\(u \ge v\))滿足存在 \(u \to v \to u\) 的環不經過編號 \(< v\) 的點。
一個點對顯然會對一個 \(G\) 的前綴造成貢獻,令 \(path(u, v)\) 表示最大化的 \(u \to v\) 的路徑上經過的邊的編號的最小值。那么 \(G_{1 \sim \min(path(u, v), path(v, u))}\) 會得到貢獻。
可以直接 Floyd \(O(n^3 + m)\) 或 Dijkstra \(O(nm \log m)\) 求出。
當然這不穩,可以考慮一個更高明的做法。枚舉 \(v\),在每張圖中只保留 \([v, n]\) 那些點,求出每張圖中它能到達的點的個數即可。
當然直接這樣模擬是 \(O(nm^2)\) 的,考慮反轉時間軸變成逆序加邊,對於一條新加的邊 \((a, b)\)(\(a, b \ge v\)):
- 如果 \(vis_a = 1\) 且 \(vis_b = 0\),那么從 \(b\) 開始 BFS;
- 如果 \(vis_a = 0\) 且 \(vis_b = 0\),那么先加到圖中,等着以后可能有用;
- 否則這條邊沒有意義。
易證每一條邊最多只會用到一次,所以在 BFS 的時候“過河拆橋”即可。時間復雜度 \(O(nm)\)。
Day2
取模
給定 \(n\) 個正整數 \(a_i\) 請你在其中選出三個數 \(i, j, k\)(\(i \ne j\),\(i \ne k\),\(j \ne k\)),使得 \((a_i + a_j) \bmod a_k\) 的值最大。
\(3 \le n \le 2 \times 10^5\),\(1 \le a_i \le 10^8\)。
首先將 \(a\) 數組排序,逆序枚舉 \(a_k\),在前面做二分或者雙指針求出 \((a_i + a_j) \bmod a_k\) 的最大值更新答案。
當 \(ans \ge a_k\) 的時候就可以 break 了,可以證明枚舉的 \(a_k\) 的個數是 \(\log\) 級別的,但我不會證。
寶石
給定一棵大小為 \(n\) 的樹以及 \(m\) 種寶石,結點 \(i\) 上有第 \(w_i\) 種寶石。
寶石收集順序是一個長度為 \(c\) 的序列 \(P\),\(P\) 中沒有重復元素。
\(q\) 次詢問,每次給出 \(s, t\),問從 \(s\) 走到 \(t\) 的過程中,所經過的結點的 \(w\) 形成的序列與 \(P\) 的最長公共子序列的長度。
\(1 \le n, q \le 2 \times 10^5\),\(1 \le c, m \le 5 \times 10^4\),\(1 \le P_i, w_i \le m\)。
將 \(s \to t\) 拆為 \(s \to \text{lca}, \text{lca} \to t\) 的兩個過程。
考慮鏈上的情況,記結點 \(u\) 的后繼 \(suc_{u}\) 為其右邊最近的滿足 \(w_v\) 在 \(P\) 序列中恰好是 \(w_u\) 的下一項的 \(v\)。
那么如果詢問的 \(s < t\),就從 \(s\) 右邊的第一個 \(w = P_1\) 的位置開始,不斷跳 \(suc\),直到越過 \(t\) 結束,跳的次數就是詢問的答案。不難發現這個過程可以倍增優化。
現在搬到樹上,\(s \to \text{lca}\) 的過程不難套用上述方法,但如果考慮 \(\text{lca} \to t\) 呢?
一個解決辦法是把過程擬過來,記錄 \(pre_{u}\) 為其父親鏈上的最近的滿足 \(w_v\) 在 \(P\) 序列中恰好是 \(w_u\) 的前一項的 \(v\)。
每次詢問的時候二分答案 \(a\),從 \(t\) 上方的最近的 \(w = P_a\) 的那一個位置開始不斷跳 \(pre\) 就行了。
至於找到“\(t\) 上方的最近的 \(w = P_x\) 的位置”,可以使用可持久化線段樹維護這個東西。
時間復雜度 \(O(q \log^2 m)\)。
滾榜
給定一個長度為 \(n\) 的數組 \(a\),求有多少 \(1 \sim n\) 的排列 \(p\),滿足存在一個 \(\sum b = m\) 的不下降序列 \(b\),使得對於任意 \(i, j\)(\(i < j\)),都有:
- \(a_{p_j} + b_{p_j} \ge a_{p_i} + b_{p_i} + [p_i < p_j]\)
- \(a_{p_i} + b_{p_i} \ge a_{p_j} + [p_j < p_i]\)
\(1 \le n \le 13\),\(1 \le m \le 500\),\(0 \le a_i \le 10^4\)。
容易發現 \(b\) 的分配是貪心的,每次給出最小的 \(b\),只要 \(\sum b \le m\) 就是合法的方案。
一個顯然的結論是“當前選手想當 rank 1 只要比上一名選手高即可”,這是因為上一名選手是當前的 rank 1。
於是可得(忽略編號問題,這個顯然是容易處理的):
- 如果 \(a_i \ge a_{i - 1}\),則 \(b_i = b_{i - 1}\);
- 如果 \(a_i < a_{i - 1}\),則 \(b_i = b_{i - 1} + a_i - a_{i - 1}\)。
綜合一下上面兩個式子其實就是 \(b_{i} = b_{i - 1} + \max(0, a_i - a_{i - 1})\)。
進而通過考慮貢獻可以知道 \(\sum b = \sum \max(0, a_i - a_{i - 1}) (n - i + 1)\)(每個貢獻都是一個后綴)。
於是考慮狀壓 DP,用 \(f(S, i, j)\) 表示,當前使用過的集合是 \(S\),最后一個選的是 \(i\),以及當前對 \(\sum b\) 的貢獻是 \(j\)。轉移的時候枚舉接下來選哪一個人,計算一下貢獻即可。
時間復雜度 \(O(2^n n^2 m)\),輕微卡常。
