<style> .reveal .slides { text-align: left; font-size:35px; } </style> ## 分治 Advanced Competitive Programming 2/17 ---- 這堂課不會講技巧,全都是例題講解。 希望大家邊聽邊想,時不時會抽人回答問題。 --- ## 例題:區間和小於t 給一個長度為$n$的序列,有幾個子區間的總和會小於$t$ $1 \leq n \leq 2*10^5$,$\left| t \right| \leq 10^{14}$ ---- 這題雖然有資結作法,但我們這裡用分治的方法想一下 想一下怎麼把小區間的答案轉移到大區間 ![image](https://hackmd.io/_uploads/BydNwwxip.png) ---- 當兩個小區間長這樣,這時候有一段連續的區間和小於$t$ ![image](https://hackmd.io/_uploads/rkENiO75a.png) ---- 不難發現你會用到左區間的後綴和加上右區間前綴和 這個例子為5+11 ![image](https://hackmd.io/_uploads/r1s8FDxoT.png) ---- 可以把左區間的後綴和排序,右區間的每個前綴和用二分搜去找左區間的有幾個後綴和可以相加<$t$ 算出來的答案數量相加為答案 ---- 分治的難點在於要去思考小區間怎麼去計算答案,然後把小區間算答案的方式帶到大區間 接下來用更多例題帶大家思考🐳 --- ## 例題:[太陽軍團(互動題)](https://neoj.sprout.tw/problem/127/) 給你一個不知道長怎樣,很大很大的$N*M$陣列,你可以多次詢問某一格的值是多少,且已知每一列的最大值出現位置都在上一列最大值的右邊,請你找出每一列的最大值出現在哪裡。 $2 \leq N \leq M \leq 10^6$,最多詢問$10^8$次 ![image](https://hackmd.io/_uploads/rJEFyYXca.png =400x) ---- 直接暴力$O(NM)$檢查每個格子顯然是不行的 要怎麼用分治來解這題呢,給大家幾分鐘時間想一下 ---- 我們可以先找中間那排,用一次$O(M)$掃過去,找最大值在哪 ![image](https://hackmd.io/_uploads/Sy5FzFX5T.png =700x) ---- 因為這題的最大值有單調性的限制,所以右上角和左下角絕對不會有最大值,剩下紅色部分的陣列要找 ![image](https://hackmd.io/_uploads/HJWB7t7cT.png =700x) ---- 一直二分下去,複雜度是$O(MlogN)$ ![image](https://hackmd.io/_uploads/rkM4NKQ9p.png =700x) --- ## 例題:[糟糕陣列](https://neoj.sprout.tw/problem/128/) 給你一個$N$,你要生出$N*N$的二維陣列,對於所有$i$ $(1 \leq i \leq N)$,使得「第$i$列的所有數值」和「第$i$行的所有數值」聯集起來為{$1,2,...,2N-1$} $(1 \leq N \leq 1024)$,存在一正整數$K$使得$N=2^k$ ![image](https://hackmd.io/_uploads/HyGckcXcp.png =350x) ---- 一樣先想一下怎麼分治:D,你可以隨便生一個$4*4$的陣列放數字看看,也可能需要通靈一下 ![image](https://hackmd.io/_uploads/BkEjqKyoT.png =350x) ---- 試著把一個數字放在(i,i)的位置 ![image](https://hackmd.io/_uploads/SkJ5cKJoT.png =350x) ---- 再把一個數字亂放在某個位置上,像是把2放在紅色的位置,會有以下兩種可能,那我們要取哪種呢 ![image](https://hackmd.io/_uploads/S1_FitJsT.png) ---- 既然我們分治的觀念是解決小問題合併到大問題 那相同的小問題重複越多越好,我們寫code就不用思考太多 我們盡量把左上四塊、右下四塊長得一樣 ![image](https://hackmd.io/_uploads/ryaD2K1ja.png =350x) ---- 無腦填3,剩下[4,5,6,7] ![image](https://hackmd.io/_uploads/SyfXTtJjp.png =350x) ---- 隨便填4,假設放在下面紅色的位置,會有兩種可能 ![image](https://hackmd.io/_uploads/SytpTKyiT.png) ---- 經過一番亂填之後,上面的這兩種可能 可能會長得像這樣 ![image](https://hackmd.io/_uploads/rJurMcJjp.png) 看起來好像都是對的,那哪個比較好做、有規律呢? ---- 對於左邊的圖,你要去紀錄左下角$2*2$方格的4,5,6,7放在哪裡 再去放右上角$2*2$方格的4,5,6,7 左下角和右上角的排列方式會不太一樣 ![image](https://hackmd.io/_uploads/HkDZzcJsT.png =350x) ---- 反觀右邊的圖 左下角$2*2$方格只有4,5兩種數字,右上角$2*2$方格只有6,7兩種數字 並且排列方式基本一模一樣 ![image](https://hackmd.io/_uploads/ryn0J5yoT.png =350x) ---- 既然排列方式一模一樣,不難發現這可能就是解決這題的一種方法 ---- 可以試試在這個二維方格切兩刀,分成四個$2*2$方格 在1~7這些數字中 選1,2,3到左上右下、4,5到左下、6,7到右上 按照規律去排數字 ![image](https://hackmd.io/_uploads/BJpAxc1ia.png =350x) ---- 因為每次都是分成大小一樣的四塊,往下遞迴左上左下右上三塊,加上要填完所有的格子,複雜度為$O(3^{logN}+N^2)$ --- ## 例題:[Max to the right Min](https://codeforces.com/problemset/problem/1849/E) 給一個長度為$n$的序列,序列為$1$~$n$的permutation,有幾個子區間符合「區間中最大的數字出現在區間中最小的數字右邊」。 ---- 思考時間:D 先透露這裡分治要二分,如果說兩個小區間的答案已經算完了,目前只要思考如何計算跨越兩個區間的答案就好了 可以仔細想一下有哪些case要考慮? ---- 1. 最大在右邊,最小在左邊 ![image](https://hackmd.io/_uploads/SkzPQSE9T.png =300x) 2. 最大和最小值都在左邊 ![image](https://hackmd.io/_uploads/S1JeA1Fq6.png =300x) 3. 最大和最小值都在右邊 ![image](https://hackmd.io/_uploads/Byo2MB4qa.png =300x) 你想到了嗎:D ---- 不難發現你可能會需要前綴、後綴、最小值、最大值這些東西 所以我們弄出來左區間的後綴最小最大值、右區間的後綴最小最大值 ![image](https://hackmd.io/_uploads/HkoIqUVcp.png) ---- 先來處理第一個case 對於一個合理的區間$[L',R']$,需要符合兩個情況 - 後綴最小值 $_{L'} <$ 前綴最小值 $_{R'}$ - 後綴最大值 $_{L'} <$ 前綴最大值 $_{R'}$ ![image](https://hackmd.io/_uploads/rkKKjLN9a.png =500x) ---- 因為這些前綴後綴最小最大值們都有單調性,你要找符合兩個情況的數字可以用二分搜分別去找。 ![image](https://hackmd.io/_uploads/HJik68E9T.png) ---- 那他們交集的位置就是L'可能的位置 你可以掃過右區間的每個位置當作R',做一遍。 這個case就解決了 ![image](https://hackmd.io/_uploads/By5kCLE9a.png) ---- 關於第二個case,最大和最小值都在左邊,看一下合理的區間[L',R']有什麼性質 ![image](https://hackmd.io/_uploads/rkB2xgK96.png) ---- 沒錯!!!!!就是 - 後綴最小值 $_{L'} <$ 前綴最小值 $_{R'}$ - 後綴最大值 $_{L'} >$ 前綴最大值 $_{R'}$ ![image](https://hackmd.io/_uploads/HJFDVeFqT.png) ---- 所以其實跟第一個case一樣可以利用二分搜找到交集的位置 還要多判斷左區間的Max有沒有在左區間的Min右邊 第三個case也是大同小異。 ![image](https://hackmd.io/_uploads/Skxj4lKca.png) ---- 總體複雜度是$O(nloglogn)$,上述所講的二分搜可以用雙指針優化成$O(nlogn)$,大家可以自己想一下怎麼移指針:D --- ## 例題:[好的連續子序列😈](https://neoj.sprout.tw/problem/788/) 給一個長度為$n$的序列,序列為$1$~$n$的permutation,有幾個子區間里的元素排序之後是公差為$1$的等差數列。 $1 \leq n \leq 5\cdot 10^5$ ---- 一樣,思考時間:D 這裡給點提示,定義$Max$是子區間的最大值、$Min$是子區間的最小值: - 一個區間$[L,R]$要滿足: $R-L+1=$$Max$$-$$Min$$+1=$區間長度 - 橫跨中間的子區間又要分成幾種情況? ---- 誒,好像跟上一題類似,這裡分4個case 1. 最小值和最大值都在左區間 ![image](https://hackmd.io/_uploads/rJK815L9p.png =350x) 2. 最小值和最大值都在右區間 ![image](https://hackmd.io/_uploads/BkCwk98qa.png =350x) ---- 3. 最小值在右區間,最大值在左區間 ![image](https://hackmd.io/_uploads/HJ8Yx9Ic6.png =400x) 4. 最小值在左區間,最大值在右區間 ![image](https://hackmd.io/_uploads/ryObx589a.png =400x) 你想到了嗎:) ---- 先講第一個case,講完之後你就會做第二個case了 一樣需要前綴後綴最大值最小值 用下面的區間來舉例 ![image](https://hackmd.io/_uploads/ry93o5U5p.png) ---- 第一個case為:「最大值和最小值都在左區間」 你可以先決定一個$L'$指針代表一個合法的區間左界觀察看看 假設$L'$在下圖的這個位置 ![image](https://hackmd.io/_uploads/Sy24AcU96.png) ---- 既然這個case叫:「最大值和最小值都在左區間」 那圖中已經赤裸裸地跟你講了最大值和最小值應該要是多少 ![image](https://hackmd.io/_uploads/BJs1eoIqT.png) ---- 提示一:$R-L+1=$$Max$$-$$Min$$+1$ 有了最大值、最小值、以及$L'$在哪個地方。 區間長度和、$R'$的位置就可以得出來了 ![image](https://hackmd.io/_uploads/rJRrmoU9p.png) ---- 要滿足最大值和最小值都在左邊 那右邊一定不能出現更大的值或更小的值 所以要去檢查$R'$的前綴最大值和最小值 ![image](https://hackmd.io/_uploads/B1EcEsLc6.png) ---- 經過檢查之後發現他是一個合理的區間 你可以把$L'$從$L$到$mid$計算一遍 那第二個case也是相同道理 ---- 好的,相對難的case要來了 接著是第三個case:「最小值在右區間,最大值在左區間」 觀察這兩個區間,用一個合理的$[L',R']$,來想想要怎麼做 ![image](https://hackmd.io/_uploads/SykCmnv9a.png) ---- 首先,既然最小值在右區間,最大值在左區間的話 代表 - 後綴最小值 $_{L'} >$ 前綴最小值 $_{R'}$ - 後綴最大值 $_{L'} >$ 前綴最大值 $_{R'}$ ![image](https://hackmd.io/_uploads/r1GtE2wcp.png) ---- 會發現好像跟上一題一樣 可以用雙指針或二分搜找到合理的區間 圖中的例子可以找到兩個合理的$R'$ ![image](https://hackmd.io/_uploads/SkE-UhD5T.png) ---- 這時候有一個問題? 兩個$R'$都可以採用嗎? ![image](https://hackmd.io/_uploads/SkE-UhD5T.png) 你會發現偏左的<!-- .element: class="fragment" data-fragment-index="1" -->$R'$<!-- .element: class="fragment" data-fragment-index="1" -->不能是一個合理的<!-- .element: class="fragment" data-fragment-index="1" -->$R'$<!-- .element: class="fragment" data-fragment-index="1" --> 它不滿足<!-- .element: class="fragment" data-fragment-index="1" -->$R-L+1=$<!-- .element: class="fragment" data-fragment-index="1" -->$Max$<!-- .element: class="fragment" data-fragment-index="1" -->$-$<!-- .element: class="fragment" data-fragment-index="1" -->$Min$<!-- .element: class="fragment" data-fragment-index="1" -->$+1$<!-- .element: class="fragment" data-fragment-index="1" -->這個式子<!-- .element: class="fragment" data-fragment-index="1" --> ---- $R-L+1=$$Max$$-$$Min$$+1$這個式子我們可以化簡成 $R+Min=L+Max$ 因此我們要找哪些$R'$滿足上面這個式子 ---- 由於$L'$已經確定了,代表$L'+Max$為一個定值 右邊有很多種$R'+Min$,你可以用一個陣列$cnt$紀錄每種$R'+Min$的次數,然後找$cnt[L'+Max]$就好了 --- 來練習8 [題目連結](https://vjudge.net/contest/605791)
{"title":"分治(D&C)","contributors":"[{\"id\":\"08326fa4-ca9b-4ca9-873e-239ebe76662c\",\"add\":9912,\"del\":3024}]"}
    402 views
   Owned this note