題解 loj3265 3266 3267 USACO 2020.2 Platinum(全)
loj3265 「USACO 2020.2 Platinum」Delegation
因為是最大化最小值,考慮二分答案。
設當前二分的答案為\(K\)。則要判斷是否有一種划分方式,使得每條鏈的長度都至少為\(K\)。
不妨以\(1\)為根,對整棵樹dfs。記\(fa(u)\)為\(u\)的父親節點。dfs(u)
函數求出一個值\(f(u)\),或判斷在當前的\(K\)下無解。有解時,我們把\(u\)的子樹划分為若干條長度\(\geq K\)的鏈,並選擇一條未完結的鏈(允許這條鏈長度\(<K\))覆蓋\(u\)和\(fa(u)\)之間這條邊。這條鏈會被交給dfs(fa(u))
繼續處理。而\(f(u)\),就是這條返回給\(fa(u)\)的鏈的最長長度。dfs(u)
函數要做的,就是在保證其他每條鏈長度都\(\geq K\)的前提下,讓\(f(u)\)的長度盡可能大。
考慮dfs(u)
函數的實現。先遞歸\(u\)的所有兒子,每個兒子\(v\)會帶來一條長度為\(f(v)+1\)的鏈(這個\(+1\)就是\(u,v\)之間的邊,它沒有被算在\(f(v)\)中)。把得到的這些鏈按長度排序。此時我們有兩種選擇:
- 方案一:把所有這些鏈兩兩匹配。(如果鏈的數量是奇數,就加一條長度為\(0\)的鏈)。要求每對匹配鏈的長度和\(\geq K\)。並令\(f(u)=0\)。
- 方案二:挑出一條鏈作為\(f(u)\),讓其余的鏈兩兩匹配。如果能匹配成功,則令\(f(u)=\)這條挑出來的鏈的長度。
這里的“兩兩匹配”,我們可以做一個簡單的貪心:讓最大的鏈和最小的鏈匹配,第\(2\)大的鏈和第\(2\)小的鏈匹配......。如果存在某一對鏈的長度和\(<K\),說明匹配失敗,無法找到合法的匹配方案。
當\(u=1\)時,我們顯然只能選擇方案一,即把所有鏈都匹配起來。否則無解。
當\(u\neq 1\)時,本着讓\(f(u)\)盡可能大的原則,我們優先考慮方案二。如果無法實現方案二,再考慮方案一是否可行。若也不可行,則無解。
現在的問題是,如果選擇方案二,我們該如何在保證其它鏈能夠成功匹配的前提下,挑出一條盡可能長的鏈作為\(f(u)\)呢?考慮兩條長度分別為\(x,y\)的鏈,若\(x<y\),則若挑出\(y\)這條鏈后其它鏈能夠成功匹配,挑出\(x\)后其他鏈也一定能成功匹配(這相當於把匹配中的\(x\)換成\(y\),有一條鏈變得更長了,匹配結果不會變差)。故可以二分把那條鏈作為\(f(u)\),判斷是否可行即可。
時間復雜度\(O(n\log^2n)\)。
loj3266 「USACO 2020.2 Platinum」Equilateral Triangles
圖片來源:洛谷用戶:ix35
觀察一個曼哈頓等邊三角形:
紅線、藍線分別是\(BC,AC\)的曼哈頓距離。
把線段平移,得到下圖:
此時,\(BC\)的曼哈頓距離是\(\color{red}{\text{紅}}\)\(+\)\(\color{green}{\text{綠}}\),\(AC\)是\(\color{blue}{\text{藍}}\)\(+\)\(\color{green}{\text{綠}}\),於是我們可以得到:\(\color{red}{\text{紅}}\)\(+\)\(\color{green}{\text{綠}}\)\(=\)\(\color{blue}{\text{藍}}\)\(+\)\(\color{green}{\text{綠}}\),所以\(\color{red}{\text{紅}}\)\(=\)\(\color{blue}{\text{藍}}\)。同理可知:\(AO=BO=CO\)(曼哈頓距離)。也就是說,\(O\)是\(\Delta ABC\)在曼哈頓距離意義上的“外心”。
考慮枚舉這個外心\(O\),再枚舉\(O\)到\(A,B\)的距離\(r\)。大力討論\(A,B\)所在的象限(四種情況)。此時\(A,B\)的位置就已經確定了。考慮\(C\)。首先\(C\)一定在和\(A,B\)相反的象限,且\(OC=r\)。可以發現這樣的\(C\)一定在一條斜\(45^{\circ}\)角的線上(即平行於矩形某條對角線的線)。對每條這樣的斜線做前綴和即可\(O(1)\)查詢出\(C\)的數量。
時間復雜度\(O(n^3)\)。
loj3267 「USACO 2020.2 Platinum」Help Yourself
把所有線段按左端點排序。設\(dp_0[i][r]\)表示考慮了前\(i\)條線段,最大右端點在\(r\)時,有多少滿足條件的線段子集;\(dp_1[i][r]\)表示此時所有滿足條件的線段子集,每個線段子集的並的連通塊數之和;\(dp_2[i][r]\)表示此時所有滿足條件的線段子集,每個線段子集的並的連通塊數的平方,之和......。
一般地,定義\(dp_k[i][r]\)表示考慮了前\(i\)條線段,最大右端點在\(r\)時,所有滿足條件的線段子集,每個線段子集的並的連通塊數的\(k\)次方之和(\(0\leq k\leq K\))。設對於一個線段集合\(s\),\(\operatorname{maxendpos}(s)\)表示\(s\)中所有線段的最大右端點,\(cnt(s)\)表示\(s\)的並的連通塊數。則:
考慮新加入一個線段\(i\)。從\(dp[i-1][j]\)轉移到\(dp[i][?]\)。分三種情況討論:
- \(j<l_i\),此時加入線段\(i\)會使右端點變為\(r_i\),且連通塊數\(+1\)。
- \(l_i\leq j\leq r_i\),此時加入線段\(i\)會使右端點變為\(r_i\),且連通塊數不變。
- \(j>r_i\),此時加入線段\(i\)既不改變右端點也不改變連通塊數。
發現問題主要在於連通塊數\(+1\)時的轉移不好處理。考慮現在有一\(dp_k[i][j]\),把它的連通塊數\(+1\),看它的值會如何變化:
由此,此時再考慮\(dp_k[i][?]\)的轉移式。初始時,令每個\(dp_k[i][j]=dp_k[i-1][j]\),表示在線段集合中不選線段\(i\)的情況。然后考慮選線段\(i\)的情況:
其中\(trans(dp_k[i-1][j])\)可以\(O(K)\)求。故時間復雜度為\(O(n^2K^2)\)。
考慮優化。
首先,\(\sum_jtrans(dp_k[i-1][j])\)就等於\(trans(\sum_jdp_k[i-1][j])\)。因為我們在推\(trans\)時並沒有用到\(j\)具體的值,只是用\(i,j\)來表示了一堆“線段的集合”。把這些集合先並起來(作為一個更大的集合),再轉移也是一樣的。
根據套路,不難想到用線段樹去維護所有的\(j\)。線段樹上,設一個節點所代表的區間為\([l,r]\)。我們在這個節點上存\(K+1\)個值,分別為:\((\sum_{j=l}^rdp_0[i][j]),(\sum_{j=l}^rdp_1[i][j]),\dots,(\sum_{j=l}^rdp_K[i][j])\)。
則從\(i-1\)到\(i\)的轉移相當於:
- 對\(r_i\)這個位置執行線段樹單點加操作,讓它的值\(+trans(\sum_{j=0}^{l_i-1}dp_k[i-1][j])\)。其中求\((\sum_jdp_k[i-1][j])\)要用到線段樹區間求和。
- 對\(r_i\)這個位置執行線段樹單點加操作,讓它的值\(+(\sum_{j=l_i}^{r_i}dp_k[i-1][j])\)。
- 對線段樹\(r_i+1\sim 2n\)這些位置執行區間乘操作,全部\(\times2\)。
注意,所有的查詢操作要在修改操作之前進行。這樣查到的才是\(dp_k[i-1]\)的值。
時間復雜度\(O(nK^2+n\log nK)\)。