A. Sushi for Two
題意
在一個 01
序列中找出長為偶數的連續的一段使得它前一半和后一半內部分別相同,而前一半和后一半不同。
\(2\le n\le 100\ 000\)
題解
令共有 \(k\) 段連續的區間,第 \(i\) 段區間的長度設為 \(a_i\),則答案是在求 \(\max_{i=1}^{k-1}\min(a_i,a_{i+1})\)。我因為沒好好判 \(\min\) 而輕松地丟掉了 WA on pretest 4
的50分。
代碼
標簽
- 枚舉
B. Circus
題意
有 \(n\) 個人,需要把它們分成兩組,每組恰好 \(\frac n2\) 個人。每個人可能會技能 \(1\) 或技能 \(2\),一個人可能會其中一種、兩種都會或什么都不會。
要求第一組中會技能 \(1\) 的人數恰好等於第二組中會技能 \(2\) 的人數,輸出方案。
\(2\le n\le 5\ 000\)
題解
\(O(n^2)\) 的做法其實有些冗余,所以找到一種好理解的 \(O(n)\) 算法。
我們用兩個 0/1
來表示一個人會的技能。比如一個人會技能 \(1\) 而不會技能 \(2\) 則表示為 0 1
。
由於 1 1
的限制比較大,而他在第一組或第二組都必須做出貢獻,因此我們枚舉 1 1
在第一組有 \(i\) 個,假定總共有 \(d\) 個 1 1
,則欽定第二組有 \(k-i\) 個 1 1
。
此時我們首先要把貢獻較小的一邊給補成 \(\max(i,d-i)\) 個,然后會分別剩下 \(a\) 個 0 0
,\(b\) 個 0 1
,\(c\) 個 1 0
。
如果 \(b>c\),可以考慮把 \(b\) 多出來的 \(b-c\) 個塞給第一組,則不做出任何貢獻。
同理如果 \(c>b\),可以考慮把 \(c\) 多出來的 \(c-b\) 個塞給第二組,則不做出任何貢獻。
但是會有一種情況,導致 \(\max(i,d-i)+|b-c|>\frac n2\),此時就會有一些不得不做貢獻的人,理解為溢出。此時就會產生一些不得不做的貢獻,是無論如何都補不回來的。
因此,當 \(\max(i,d-i)+|b-c|\le \frac n2\) 時,就有解。我們首先把 \(i\) 個 1 1
輸出,如果 \(i<d-i\),需要先用 1 0
把它補到 \(d-i\),這也是要輸出的。然后剩下的優先選 0 1
輸出,最后拿 0 0
補齊。
代碼
標簽
- 枚舉
- 構造
C. Skyscrapers
題意
在 \(n\times m\) 的網格上,每個格子都有一個權值。現在對於每個格子 \((i,j)\),問給第 \(i\) 行和第 \(j\) 列重新賦值為正整數,使得它們的原相對大小不變,問最大的正整數最小是多少。
\(1\le n,m\le 1\ 000\)
題解
我們發現第 \(i\) 行和第 \(j\) 列僅在 \((i,j)\) 處產生聯系,其余都是獨立的。
因此對於所有元素,可以在 \(O(nm\log n)\) 的時間里知道它在那一行/那一列的排名,行和列、大的和小的都要照顧到。假設它在那一行里排名為 \(p\),那一列里排名為 \(q\),則答案為 \(\max(p,q)+\max(m-p,n-q)\)。
代碼
標簽
- 排序
- 貪心
D. Camp Schedule
題意
給定串 \(s\),代表學生的計划,其中 0
表示這一天沒寫作業,1
表示這一天寫作業了。
給定串 \(t\),代表最佳計划,其中 0
表示這一天不用寫作業,1
表示這一天要寫作業。
你可以改變串 \(s\) 的順序,最大化 \(t\) 在 \(s\) 中出現的次數。
\(1\le |s|,|t|\le 500\ 000\)
題解
可以看出給出 \(s\) 相當於給出了 \(0\) 和 \(1\) 的個數,因此我們需要考慮 \(t\) 的構造。我們發現 \(t\) 在 \(s\) 中的出現是可以重合的,因此我們求出 KMP 中的 \(nxt\) 數組,我們只取前 \(n-nxt[n]\) 個。假設前 \(n-nxt[n]\) 個字符構成的字符串為 \(u\),令 \(u\) 中 \(0\) 的個數為 \(p\),\(1\) 的個數為 \(q\);令 \(s\) 中 \(0\) 的個數為 \(a\),\(1\) 的個數為 \(b\),則至少能構成 \(\min\left(\left\lfloor\frac ap\right\rfloor,\left\lfloor\frac bq\right\rfloor\right)-1\) 個 \(t\) 。
把這些構造完后,我們還會剩下一些 \(0,1\),而且還有一個 \(t\) 是不完整的,我們盡量與 \(t\) 匹配地接在它后面。這樣輸出就可以了。
求 \(nxt\) 數組和構造時間復雜度為 \(O(\mathtt{strlen}(s))\)。
代碼
標簽
- KMP
- 構造
E. Museums Tour
題意
一周有 \(d\) 天。一個人在一周的第 \(1\) 天從 \(1\) 號點出發,城市的地圖是一個 \(n\) 個點 \(m\) 條邊的有向圖。這個人每天晚上要么結束旅行,要么往某個方向行走一步。每個城市有一個博物館,只在每周的特定時間開放。問他最多能參觀多少個城市的博物館。
\(1\le d\le 50\)
\(1\le n\le 100\ 000\)
\(1\le m\le 100\ 000\)
題解
好題。
我們考慮把每一天拆成 \(d\) 個點,令第 \(i\) 號點在第 \(j\) 天的點為 \([i,j]\)。存在 \(\left<i,v\right>\),則讓 \([i,j]\) 向 \([v,j\bmod d+1]\) 連邊。
注意:博物館不開放僅代表不做出貢獻,不代表那天不能去那個城市。因此連邊不能省。
然后我們可以在這個有向無環圖上跑 tarjan 縮點。
可以證明,在新的 DAG 上的任意一條鏈中,原圖的每個點只會出現在一個強連通分量里。
- 在連邊后的圖上,如果原圖中的點 \([i,j]\) 向 \([i,k]\) 有連邊,那么 \([i,k]\) 一定向 \([i,j]\) 有連邊。因此在路徑上可能出現的原圖同一個點的新點都會被縮到同一個強連通分量里。
因此我們可以計算每個 SCC 的貢獻。當給出的矩陣中 \([i,j]\) 為 \(1\) 時則做出 \(1\) 的貢獻,\(i\) 相同的不重復統計。
然后計算從 \([1,1]\) 開始的權值最長路。拓撲排序可以完成。
但是由於一共有 \(5\times 10^6\) 個點,遞歸可能會爆棧,因此我寫的非遞歸(碼量有點大但是不是很難寫)。
可以參考 Pinkrabbit 的遞歸超小空間解法。
代碼
標簽
- tarjan
- 拓撲排序
F. Cooperative Game
題意
交互題
現在有 \(10\) 個棋子。有一個圖,是由一條長為 \(t\) 個節點的鏈指向一個大小為 \(c\) 的有向環的。其中除了 home vertex的入度為 \(0\),finish vertex 的入度為 \(2\),其余各點的入度均為 \(1\);同時,所有點的出度都為 \(1\)。
現在,每一步你可以選取一些點讓它走一步到它出邊指向的點。走完每一步以后,交互器都會告訴你哪些棋子在同一個點上。現在要你在 \(3(t+c)\) 步以內,把所有的棋子都移動到 finish vertex。
\(1\le t,c,(t+c)\le 1\ 000\)
題解
由於之前做過的交互題都是和倍增/二進制有關,本題給出了 \(10\) 個點,而且 \(\log_2 1000\) 也恰好是 \(10\),於是就這樣陷進去了…
實際上我們只需要 \(3\) 個點就能解決本題的問題,我們可以把這 \(10\) 個點分為 \(0,1,23456789\) 三個部分。
首先讓 \(0\) 每走 \(2\) 步,\(1\) 走一步。當 \(0,1\) 相遇時環長即為 \(0\) 走過的距離減去 \(1\) 走過的距離。每次 \(0\) 總比 \(1\) 快一步。除了一種情況以外,環長也等於 \(1\) 走過的步數。這叫做Floyd判環。
一種情況:
如果下一步是 \(0,1\) 一起走的話,那么 \(0\) 走的步數就不恰好是 \(1\) 走的步數的兩倍了,因此需要判一下。
此時我們知道 \(0,1\) 在環上的某個點相遇了。我們現在知道了環長 \(c\) 和鏈長 \(t\),但是我們不知道 finish vertex 在哪里。
我們為了讓 \(01\) 與 \(23456789\) 在 finish vertex 相遇,我們可以構造出環上的一個點。設 \(1\) 號棋子已經走了 \(m\) 步,因此令 \(01\) 在環上“倒退 \(m\) 步”,也就是前進 \(-m\pmod c\) 步,此時就和 \(23456789\) 同步了。我們發現,環長一般情況下也恰好是 \(m\)(特殊情況是 \(m+1\),讓 \(01\) 先走一步就可以了),然后讓 \(0123456789\) 同時移動。
最終所有點相遇的地方就是 finish vertex 了。輸出 done
即可。
一般情況下步數為 \(2(t+c)-1\),上面提到的情況步數為 \(2(t+c)\)。
代碼
標簽
- Floyd判環
- 交互