浅谈扫描线
Preface
本文部分題目摘自 lxl 的數(shù)據(jù)結(jié)構(gòu)系列課件
由于工程量巨大,難免會(huì)出現(xiàn)錯(cuò)字、漏字的情況,如果影響到了您的閱讀體驗(yàn),還請(qǐng)私信我,我會(huì)在第一時(shí)間修復(fù)。
本文建議大家有一定的數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)后再來(lái)閱讀 /heart。
個(gè)人感覺(jué)掃描線不是難點(diǎn),難點(diǎn)在于怎么抽象模型。
首先需要明白一個(gè)東西,叫做 \(B\) 維正交范圍
在一個(gè) \(B\) 維直角坐標(biāo)系下,第 \(i\) 維的坐標(biāo)在范圍 \([l_i, r_i]\) 之內(nèi),則符合這些限制的點(diǎn)構(gòu)成的的點(diǎn)集為 \(B\) 維正交范圍。
當(dāng)每個(gè)維度都是兩個(gè)端點(diǎn)的限制時(shí),我們管它叫 2B-side 的 \(B\) 維正交范圍。
如果部分維度只有一個(gè)端點(diǎn)的限制,我們管它叫 A-side 的 \(B\) 維正交范圍。
如果有維度沒(méi)有限制,則顯然,這個(gè)維度不參與構(gòu)成正交范圍。
一維掃描線
當(dāng)處于一個(gè)靜態(tài)的二維平面上時(shí),我們不難想到用掃描線來(lái)掃描其中的一個(gè)維度,用數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)另一個(gè)維度。
在掃描線掃描的過(guò)程中(例如從左到右掃),可能會(huì)在數(shù)據(jù)結(jié)構(gòu)上產(chǎn)生一些修改和詢(xún)問(wèn)。
如果信息可差分,則直接差分,否則需要分治。
如果從序列的角度上來(lái)看的話,不難發(fā)現(xiàn)掃描線做的實(shí)際上是枚舉詢(xún)問(wèn)右端點(diǎn),維護(hù)左端點(diǎn)答案。
假設(shè)當(dāng)前的信息是一個(gè) 4-side 的信息,這個(gè)時(shí)候如果信息滿足差分,就可以把 4-side 矩形差分成 3-side,然后這個(gè)時(shí)候就可以直接做一維掃描線,我們讓掃描線掃描一個(gè) side,剩下所需要維護(hù)的信息就是一個(gè) 2-side 的信息,說(shuō)人話就是一個(gè)區(qū)間操作的信息。
靜態(tài)區(qū)間查詢(xún)類(lèi)問(wèn)題
靜態(tài)的序列,只有區(qū)間查詢(xún)。
一般維度只有 \(2\)。
二維數(shù)點(diǎn)
給定一個(gè)長(zhǎng)為 \(n\) 的序列,有 \(q\) 次詢(xún)問(wèn),每次問(wèn)你區(qū)間 \([l, r]\) 中有多少位置上的數(shù)在范圍 \([x,y]\) 之內(nèi)。
\(1 \le n,q \le 2 \times 10^5,\;1\le l \le r \le n,\; 0 \le x \le y \le 10^9\)。
首先,不難發(fā)現(xiàn)所查詢(xún)的是一個(gè) 4-side 矩形內(nèi)部的點(diǎn)的個(gè)數(shù),這類(lèi)問(wèn)題一般叫做「二維數(shù)點(diǎn)」,是一類(lèi)很經(jīng)典的問(wèn)題。
首先考慮一下怎么建系,由于題目直接把坐標(biāo)系懟你臉上了,所以不難看出可以讓一個(gè)軸表示值域維,另一個(gè)軸表示序列維,然后顯然,序列維上是容易差分的,所以在序列維上差分,將范圍從 4-side 降到 3-side,這個(gè)時(shí)候就好辦了,掃描線掃描序列維剩下的那一個(gè) side,然后在掃描線上就變成了查詢(xún) \([x,y]\) 上點(diǎn)的個(gè)數(shù),不難想到用樹(shù)狀數(shù)組解決,時(shí)間復(fù)雜度 \(O((n+q) \log n)\)。
基礎(chǔ)問(wèn)題
給定 \(n\) 個(gè)矩形,然后有 \(m\) 次詢(xún)問(wèn),每次詢(xún)問(wèn)給定一個(gè)點(diǎn),問(wèn)這個(gè)點(diǎn)被多少矩形所包含。
范圍懶得給了,反正要求單 \(\log\)。
如果站在詢(xún)問(wèn)的點(diǎn)的角度來(lái)看,似乎問(wèn)題不是很好解決。
不妨這次來(lái)試著拆一下貢獻(xiàn),看看每個(gè)矩形都貢獻(xiàn)了什么,不難發(fā)現(xiàn)每個(gè)矩形都將這個(gè)矩形內(nèi)的點(diǎn)被套的次數(shù) \(+1\)。
這樣就爽了,考慮直接建系,橫軸就是題目中的 \(x\) 軸,縱軸就是題目中的 \(y\) 軸,然后依舊是熟悉的配方,將 4-side 的矩形給差分成 3-side 的矩形,也就是將每個(gè)矩形拆成入邊和出邊。現(xiàn)在用掃描線掃描 \(x\) 軸(當(dāng)然,掃描 \(y\) 軸也可以),在線上維護(hù)一個(gè) 2-side 的區(qū)間,當(dāng)掃描到入邊的時(shí)候,意味著區(qū)間 \(+1\),掃描到出邊的時(shí)候,意味著區(qū)間 \(-1\),然后每個(gè)查詢(xún)的點(diǎn)對(duì)應(yīng)到線上就是單點(diǎn)查詢(xún)。
時(shí)間復(fù)雜度 \(O((n+q)\log n)\)。
P1972 [SDOI2009] HH 的項(xiàng)鏈
老典題了。
不妨先嘗試建系,讓一個(gè)軸為序列維,讓一個(gè)軸為值域維,然后你會(huì)發(fā)現(xiàn)這個(gè)不同的數(shù)的個(gè)數(shù)似乎特別不好維護(hù)。
不妨讓點(diǎn)都具有超能力,當(dāng)掃描線掃描到和他相同的值時(shí),把前驅(qū)的貢獻(xiàn)轉(zhuǎn)移到當(dāng)前位置(可以認(rèn)為點(diǎn)直接瞬移到掃描線所在的位置上),這樣的轉(zhuǎn)化就不難將原問(wèn)題變成一個(gè) 2-side 的區(qū)間了,考慮維護(hù)一個(gè) now 數(shù)組,表示當(dāng)前這個(gè)點(diǎn)所在的位置。然后掃描序列維(對(duì)應(yīng)詢(xún)問(wèn) \(r\)),在線上也維護(hù)序列維(對(duì)應(yīng)詢(xún)問(wèn) \(l\)),表示當(dāng)前位置貢獻(xiàn)了多少點(diǎn),則不難發(fā)現(xiàn)這是一個(gè)「單點(diǎn)加/減」和「區(qū)間查」的需求,用樹(shù)狀數(shù)組維護(hù)即可,最后考慮每次向右掃描的時(shí)候,更新 now 數(shù)組和線上的樹(shù)狀數(shù)組,此時(shí)時(shí)間復(fù)雜度為 \(O((n+q)\log n)\)。
BZOJ 3489 A simple RMQ problem(弱化版)
給定長(zhǎng)為 \(n\) 的序列,\(m\) 次查詢(xún)區(qū)間中有多少值只出現(xiàn)一次。
要求單 \(\log\)。
你不難發(fā)現(xiàn)當(dāng)一個(gè)區(qū)間內(nèi)出現(xiàn)兩個(gè)相同的數(shù)時(shí),這個(gè)數(shù)就不產(chǎn)生貢獻(xiàn)了。
于是類(lèi)比上面的「HH 的項(xiàng)鏈」,我們不難想到當(dāng)我們掃描線掃到一個(gè)數(shù)時(shí),讓其貢獻(xiàn)為 \(+1\),讓其前驅(qū)的貢獻(xiàn)為 \(-1\),讓其前驅(qū)的前驅(qū)的貢獻(xiàn)為 \(0\),然后剩下的就和「HH 的項(xiàng)鏈」一樣了。
CF522D Closest Equals
考慮直接建系,設(shè)橫軸為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(r\)),縱軸也為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(l\)),然后我們?cè)跈M軸上做掃描線。
現(xiàn)在考慮掃到一個(gè)數(shù)要做什么,首先,顯然的是,至少要有一對(duì)數(shù)才有「距離」這個(gè)概念,然后我們?cè)趻呙璧臅r(shí)候都是在擴(kuò)張?jiān)儐?wèn)的右端點(diǎn),所以不妨讓相鄰兩個(gè)點(diǎn)中靠左的那個(gè)點(diǎn)的位置維護(hù)距離,這樣就變成了在線上維護(hù)「單點(diǎn)修改,區(qū)間查詢(xún)最小值」了。
時(shí)間復(fù)雜度 \(O((n+m)\log n)\)。
P4137 Rmq Problem / mex
考慮建系,設(shè)橫軸為序列維,縱軸為值域維,在序列維上做掃描線,線上維護(hù)最小值,然后掃描線的移動(dòng)就是線上的單點(diǎn)修改,查詢(xún)就是在掃描線上進(jìn)行二分,這里用線段樹(shù)維護(hù)線上的信息即可。
時(shí)間復(fù)雜度 \(O((n+m)\log n)\)。
未知渠道獲得的題
給定一棵 \(n\) 個(gè)點(diǎn)的樹(shù),然后有 \(q\) 次查詢(xún),每次查詢(xún)區(qū)間 \([l,r]\),表示詢(xún)問(wèn)當(dāng)這個(gè)樹(shù)僅剩點(diǎn)和邊的編號(hào)在 \([l,r]\) 之間時(shí)連通塊的個(gè)數(shù)。
要求單 \(\log\)。
考慮這里的連通塊個(gè)數(shù)等于啥,顯然等于「點(diǎn)數(shù) - 邊數(shù)」,然后點(diǎn)的個(gè)數(shù)是簡(jiǎn)單的(恒定 \(r - l + 1\)),因?yàn)榉秶鷥?nèi)的點(diǎn)肯定會(huì)被算入到貢獻(xiàn)之中。
但是邊不一定,因?yàn)橛锌赡芩B接了超出 \([l, r]\) 范圍的點(diǎn),這樣的邊是不能被計(jì)算進(jìn)去的,所以現(xiàn)在我們要做的處理「邊數(shù)」這一部分的貢獻(xiàn)。不妨先冷靜下來(lái),考慮影響一個(gè)邊是否是有效邊的因素有哪些,不難想到是「邊的兩個(gè)端點(diǎn)是否屬于 \([l,r]\)」和「邊的編號(hào)是否屬于 \([l,r]\)」,然后發(fā)現(xiàn)只要兩個(gè)不滿足其一,這個(gè)邊就沒(méi)有貢獻(xiàn)了。
這不失為一個(gè)非常優(yōu)秀的性質(zhì),它允許我們將限制條件轉(zhuǎn)化為讓 \(l \le \min(u,v,label)\) 且 \(\max(u,v,label) \le r\),其中假設(shè)邊 \((u,v)\) 的編號(hào)為 \(label\)。
現(xiàn)在考慮建系,設(shè)橫軸為 \(\min\) 維,縱軸為 \(\max\) 維,此時(shí)對(duì)邊的詢(xún)問(wèn)就變成了在一個(gè) 2-side 矩形里面數(shù)點(diǎn)。
時(shí)間復(fù)雜度 \(O(n \log n)\)。
BZOJ 4212 神牛的養(yǎng)成計(jì)劃
一個(gè)經(jīng)典的 trick 是對(duì)字符串建立 Trie 樹(shù),然后轉(zhuǎn)化到 DFS 序上的點(diǎn)。
此時(shí)我們不難想到對(duì)模式串正著建個(gè) Trie,記為 \(T_0\),反著建個(gè) Trie,記為 \(T_1\),然后查詢(xún)的時(shí)候?qū)τ诖?\(a\) 的限制就是在 \(T_0\) 上跑,對(duì)于串 \(b\) 的限制就是在先翻轉(zhuǎn),然后在 \(T_1\) 上跑,然后查詢(xún)滿足在 \(T_0\) 中 DFS 序范圍為 \([d_a,d_a+siz_a]\),在 \(T_1\) 中 DFS 序范圍為 \([d_b,d_b+siz_b]\) 的有效交的大小。
這個(gè)時(shí)候我們開(kāi)始建系,考慮設(shè)橫軸為 \(T_0\) 的 DFS 序列,縱軸為 \(T_1\) 上的 DFS 序列,則每個(gè)字符串的每個(gè)字符都對(duì)應(yīng)了平面上的一個(gè)點(diǎn),然后查詢(xún)就是在 4-side 的矩形內(nèi)數(shù)點(diǎn)。
由于是強(qiáng)制在線,所以把掃描過(guò)程扔到主席樹(shù)上,此時(shí)依然是單 \(\log\) 的。
時(shí)間復(fù)雜度 \(O(L_2 + m\log L_1)\)。
UOJ 637. 【美團(tuán)杯2021】A. 數(shù)據(jù)結(jié)構(gòu)
Trick Point:
這種查有多少元素的題一般都考慮利用不同值對(duì)答案貢獻(xiàn)獨(dú)立的性質(zhì)。
先考慮對(duì)每個(gè)元素計(jì)算一下其對(duì)哪些詢(xún)問(wèn)有貢獻(xiàn),然后使用數(shù)據(jù)結(jié)構(gòu)批處理貢獻(xiàn)。
考慮每個(gè)元素對(duì)答案的貢獻(xiàn),有一種常見(jiàn)方法是考慮對(duì)于什么詢(xún)問(wèn)這個(gè)元素對(duì)答案沒(méi)有貢獻(xiàn)。
如果一個(gè)出現(xiàn)了 \(k\) 次的數(shù)所影響的范圍可以用 \(O(k)\) 個(gè)矩形表示出來(lái),我們也就解決了這個(gè)問(wèn)題,因?yàn)?\(\sum k = n\)。
由于一個(gè)數(shù) \(x\) 出現(xiàn)多次和出現(xiàn)一次的情況是完全一樣的,所以不妨考慮一下什么時(shí)候這個(gè)數(shù)它消失了。簡(jiǎn)單而快速的,我們知道只有兩個(gè)條件:
- 數(shù) \(x\) 全都被 \(+1\) 了。
- 數(shù) \(x-1\) 全沒(méi)被 \(+1\)。
現(xiàn)在需要將這些限制條件轉(zhuǎn)變成平面上的限制條件,考慮建系,設(shè)橫軸為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(l\)),橫軸也為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(r\)):
-
數(shù) \(x\) 全部被 \(+1\) 了。
簡(jiǎn)單的,維護(hù)一下數(shù) \(x\) 第一次出現(xiàn)的位置 \(L\) 和最后一次出現(xiàn)的位置 \(R\),那么顯然當(dāng)詢(xún)問(wèn) \([l,r]\) 存在 \(l \le L\) 且 \(r \ge R\) 的時(shí)候?qū)^(qū)間沒(méi)有貢獻(xiàn)。
-
數(shù) \(x-1\) 全沒(méi)有被 \(+1\)。
考慮數(shù) \(x-1\) 相鄰兩次出現(xiàn)的位置 \(i,j\),則顯然的是 \([i+1,j-1]\) 這一段內(nèi)是不存在 \(x-1\) 的。
對(duì)應(yīng)到二維平面上,就是矩形 \([i+1,j-1] \times [i+1,j-1]\)。
換句話說(shuō),所有 \(x-1\) 不出現(xiàn)的位置構(gòu)成了 \(O(cnt_x + 1)\) 個(gè)矩形。
然后不難發(fā)現(xiàn),限制 \(1\) 的本質(zhì)上就是將限制 \(2\) 算出的矩形切掉了一部分,所以不影響漸進(jìn)意義上的矩形個(gè)數(shù)。
考慮此時(shí)的詢(xún)問(wèn)就變成了平面上的若干點(diǎn),這時(shí)候我們將問(wèn)題轉(zhuǎn)化為上面的「基礎(chǔ)問(wèn)題」,做刪貢獻(xiàn)掃描線即可。
時(shí)間復(fù)雜度 \(O((n+m)\log n)\)。
百度之星 2021 初賽第三場(chǎng) 1008
給定一個(gè)長(zhǎng)度為 \(n\) 的序列 \(A\),下標(biāo)從 \(1\) 到 \(n\),有 \(m\) 次查詢(xún)操作,每次給出一個(gè)區(qū)間 \([l,r]\),求一個(gè)子區(qū)間 \([l',r']\),滿足 \(l \le l' \le r' \le r\),使得 \([l',r']\) 中的顏色數(shù)比 \([l,r]\) 中出現(xiàn)的顏色數(shù)少,且其長(zhǎng)度 \(r'-l'+1\) 最大。如果 \(l' > r'\),我們認(rèn)為沒(méi)有值在 \([l',r']\) 中出現(xiàn)過(guò)。
\(1\le n,m,a_i\le2\times 10^6\),時(shí)限 \(8s\)。
首先感謝高效率原題自動(dòng)機(jī) do_while_true 為我找到了原題鏈接。
考慮答案會(huì)是什么樣子,不難想到要么是一個(gè)前綴,要么是一個(gè)后綴,要么是一個(gè)中間一部分的子區(qū)間。
前綴咋做呢,顯然的,只要找到了最靠右出現(xiàn)第一次出現(xiàn)的那個(gè)數(shù)的位置,在這個(gè)位置之前的都是合法的答案,后綴同理。這兩個(gè)「掃描線 + 樹(shù)狀數(shù)組 + 倍增」都是輕松做的,在這里不多贅述。
至于「中間一部分的子區(qū)間」這一類(lèi)情況,套用 UOJ 637 那道題的 trick,考慮「以 \(r\) 為橫坐標(biāo),\(l\) 為縱坐標(biāo)」建系,設(shè) \(a\) 為顏色 \(c\) 出現(xiàn)的位置之一,\(b\) 為其后繼,則任何一個(gè)嚴(yán)格包含 \([a+1,b-1]\) 的區(qū)間都可以去除區(qū)間 \([a+1,b-1]\) 以外的部分來(lái)獲得 \(b - a - 1\) 的貢獻(xiàn),然后可以這樣做的 \(L,R\) 只有 \(L \in [1,a], R \in [b + 1, n]\),對(duì)應(yīng)到平面上就是一個(gè) 2-side 矩形,掃描線上維護(hù)「后綴 \(\max(x, a_i)\),單點(diǎn)查」即可,發(fā)現(xiàn)這個(gè)玩意可能要 Segbeats,進(jìn)一步觀察性質(zhì),發(fā)現(xiàn)每一次修改的后綴都不一樣,然后維護(hù)的信息可以對(duì)偶成「單點(diǎn)修改,前綴 \(\max\)」,所以用樹(shù)狀數(shù)組維護(hù)即可。
時(shí)間復(fù)雜度 \(O((n+m)\log n)\)。
UVA1608 Non-boring sequences
唯一的難點(diǎn)在于建系。
假設(shè)一個(gè)數(shù)的前驅(qū)、后繼的位置為 \(i,j\),考慮這個(gè)數(shù)能支配的最長(zhǎng)區(qū)間是多大,則不難發(fā)現(xiàn)是 \([i+1,j-1]\),不然的話就會(huì)出現(xiàn)相同的兩個(gè)數(shù)。
然后你發(fā)現(xiàn)如果這樣直接在序列上統(tǒng)計(jì)的話似乎不直觀,而且好像還算重了很多部分,所以不妨把它放到二維平面上。具體的講,就是以橫軸為序列維,縱軸也為序列維建系,然后此時(shí)一個(gè)點(diǎn)所支配的就是一個(gè)矩形 \([i+1,j-1] \times [i+1,j-1]\),則此時(shí)考慮做「矩形面積并」,然后判斷算得的面積是否與整個(gè)平面的面積 \(\dfrac{n(n+1)}{2}\) 相等即可。
時(shí)間復(fù)雜度 \(O(n\log n)\),注意別忘了這個(gè)題還是多測(cè)。
經(jīng)典問(wèn)題
有一個(gè)長(zhǎng)度為 \(n\) 的序列,有 \(m\) 次詢(xún)問(wèn),每次詢(xún)問(wèn)一個(gè)區(qū)間,問(wèn)區(qū)間上出現(xiàn)奇數(shù)次數(shù)的數(shù)的異或和為多少。
要求線性。
根據(jù)異或的性質(zhì),發(fā)現(xiàn)當(dāng)一個(gè)數(shù)異或偶數(shù)次自己,貢獻(xiàn)為 \(0\)。
所以原問(wèn)題可以轉(zhuǎn)化成區(qū)間異或和,考慮做異或前綴和即可。
時(shí)間復(fù)雜度 \(O(n+m)\)。
不那么經(jīng)典的問(wèn)題
有一個(gè)長(zhǎng)度為 \(n\) 的序列,有 \(m\) 次詢(xún)問(wèn),每次詢(xún)問(wèn)一個(gè)區(qū)間,問(wèn)區(qū)間上出現(xiàn)偶數(shù)次數(shù)的數(shù)的異或和為多少。
要求單 \(\log\)。
根據(jù)「經(jīng)典問(wèn)題」,我們知道了出現(xiàn)奇數(shù)次怎么做,但是感覺(jué)偶數(shù)次似乎很難做的樣子。
考慮正難則反,由于異或的性質(zhì)「自己為自己的逆運(yùn)算」,所以只要能夠統(tǒng)計(jì)出「區(qū)間內(nèi)出現(xiàn)的數(shù)的異或和」,然后再異或上「區(qū)間異或和」,我們就得到了出現(xiàn)偶數(shù)次數(shù)的數(shù)的異或和。
那對(duì)于這種區(qū)間顏色單貢獻(xiàn)有關(guān)的部分,容易想到「HH 的項(xiàng)鏈」,顯然這個(gè)題可以類(lèi)比「HH 的項(xiàng)鏈」,當(dāng)我們掃描到一個(gè)數(shù)的時(shí)候,讓這個(gè)數(shù)的前驅(qū)的貢獻(xiàn)變?yōu)?\(0\),然后做查詢(xún)即可。
時(shí)間復(fù)雜度 \(O((n+m) \log n)\)。
CF1083D The Fair Nut's getting crazy
假設(shè) \(a \le c \le b \le d\),不難發(fā)現(xiàn)此時(shí)的 \(a\) 為 \(\le c\) 里面的一段后綴,\(d\) 為 \(\ge b\) 里面的一段前綴。
考慮維護(hù)序列 \(A\) 每個(gè)位置值的前驅(qū)和后繼,記為 \(pre_i\) 和 \(suf_i\),此時(shí)進(jìn)一步縮放 \(a\) 和 \(d\) 的范圍,則 \(a\) 的可能取值被限制在了 \((\max_{i=c}^b pre_i,c]\),\(d\) 被限制在了 \([b,\min_{i = c}^b suf_i)\),現(xiàn)在我們滿足了題目的所有要求,此時(shí)答案為 \((c - \max_{i=c}^b pre_i) \times (\min_{i=c}^b suf_i - b)\),將式子拆開(kāi),得到 \(c\min - cb - \min\max + b\max\)。
建系,設(shè)橫軸為 \(b\) 的可能取值,縱軸為 \(c\) 的可能取值,掃描線在橫軸上從左向右跑,跑的同時(shí)維護(hù)兩個(gè)維護(hù)了 \(pre\) 和 \(suf\) 的后綴最大值的單調(diào)棧,掃描線向右擴(kuò)展的時(shí)候向兩個(gè)單調(diào)棧分別加入的 \(pre_{b'}\) 和 \(suf_{b'}\), 考慮此時(shí)會(huì)彈出若干元素,這個(gè)時(shí)候計(jì)算這些元素的貢獻(xiàn)即可,具體地說(shuō),就是線段樹(shù)上區(qū)間更新 \(\min\) 部分和 \(\max\) 部分,簡(jiǎn)單的,考慮打兩個(gè)標(biāo)記 \(mntag\) 和 \(mxtag\) 即可,然后每一次做一次全局查詢(xún)加到 \(ans\) 里面即可。
時(shí)間復(fù)雜度 \(O(n \log n)\)。
CF793F Julia the snail
考慮離線,設(shè)橫軸為詢(xún)問(wèn)右端點(diǎn),線上維護(hù)詢(xún)問(wèn)左端點(diǎn),設(shè) \(f_i\) 表示從 \(i\) 開(kāi)始能走到的最高的距離,線上維護(hù)數(shù)列 \(f\),然后不難發(fā)現(xiàn)遇到一個(gè)新的繩子時(shí)所有 \(f_i\ge l\) 的地方全部都能變?yōu)楫?dāng)前的 \(r\),然后詢(xún)問(wèn)變成單點(diǎn)詢(xún)問(wèn)即可,考慮這個(gè)東西做「Segment tree beats」即可。
由于沒(méi)有區(qū)間加,所以時(shí)間復(fù)雜度 \(O((n + q) \log n)\)。
CF407E k-d-sequence
首先特判 \(d = 0\),做法簡(jiǎn)單,找到最長(zhǎng)的連續(xù)段即可。
現(xiàn)在考慮 \(d \not= 0\),發(fā)現(xiàn)一個(gè)數(shù)列想要滿足題目條件必須滿足以下條件:
- 區(qū)間內(nèi)所有數(shù)模 \(d\) 均為一個(gè)數(shù)。
- 不出現(xiàn)重復(fù)的數(shù)字。
- \(\dfrac{\max - \min}ozvdkddzhkzd \le r - l + k\)。
可以將序列分成若干個(gè)「模 \(d\) 連續(xù)段」,這樣的話第一個(gè)限制就消失了。
現(xiàn)在需要對(duì)每個(gè)連續(xù)段考慮限制 \(2,3\) 怎么做。
考慮掃描線掃描序列右端點(diǎn),線上維護(hù)序列左端點(diǎn),即在線上維護(hù) \(\dfrac{\max - \min}ozvdkddzhkzd - r + l - k\),考慮 \(k\) 可以當(dāng)做常數(shù),\(l,r\) 可以被轉(zhuǎn)化為區(qū)間加,難點(diǎn)在于前面,簡(jiǎn)單的,用一個(gè)單調(diào)棧維護(hù)一下 \(\max\) 和 \(\min\) 即可,在彈出的時(shí)候做區(qū)間加就好了,查詢(xún)二分一下即可。
考慮限制 \(2\),假設(shè)當(dāng)前右端點(diǎn)位置為 \(r\),上面的數(shù)位 \(a_r\),那么和 \(a_r\) 相同的位置必定是左端點(diǎn)不能跨越的,這樣使得這個(gè)東西變成了一個(gè)「首端刪除、末端加入」的操作。
綜上,我們需要在線上維護(hù)「區(qū)間加、首端刪除、末端加入,區(qū)間上二分」這些操作,簡(jiǎn)單的,用線段樹(shù)搞一下就好了。
時(shí)間復(fù)雜度 \(O(n \log n)\)。
CF453E Little Pony and Lord Tirek
考慮每一次詢(xún)問(wèn)都會(huì)導(dǎo)致一部分直接推平,然后推平后就成了簡(jiǎn)單的分段函數(shù)型(一次函數(shù)與常值函數(shù))了,我們?cè)O(shè)時(shí)間為每個(gè)位置的顏色,則此時(shí)每一次區(qū)間查詢(xún)就被我們轉(zhuǎn)換到區(qū)間染色,這樣貢獻(xiàn)被拆成了若干染色段的同時(shí)也方便我們維護(hù)時(shí)間差。
考慮 \(m,r\) 的范圍比較小,仔細(xì)算算我們最多最多只需要維護(hù) \(1e5\) 長(zhǎng)度的時(shí)間,這樣就好辦了,考慮掃描線掃描序列維,然后分段函數(shù)就被我們變成了在時(shí)間維上的等差數(shù)列加(當(dāng)函數(shù)值 \(\le m\) 時(shí))和區(qū)間染色(當(dāng)函數(shù)值 \(\ge m\) 時(shí)),然后查詢(xún)是一個(gè)單點(diǎn)的查詢(xún),所以不難想到進(jìn)行一個(gè)差分,然后將「等差數(shù)列加」和「區(qū)間染色」轉(zhuǎn)變成「區(qū)間加」和「單點(diǎn)加」,然后將單點(diǎn)查變?yōu)椤竷纱吻熬Y和的差」。
時(shí)間復(fù)雜度 \(O((n+q)\log n)\)。
P5490 【模板】掃描線(矩形面積并)
(好好好,現(xiàn)在才做模板題是吧)
由于出題人直接把坐標(biāo)系懟在我們臉上了,只需要考慮掃描線的部分就好了。
把矩形拆成入邊和出邊,然后在 \(x\) 軸上跑掃描線,此時(shí)在線上維護(hù)「區(qū)間 \(\pm1\),全局 \(>0\) 位置數(shù)」,考慮使用線段樹(shù)來(lái)維護(hù)。
這里的線段樹(shù)略微有一點(diǎn)點(diǎn)技巧,我們可以維護(hù)區(qū)間 \(\min\) 值和區(qū)間 \(\min\) 值出現(xiàn)次數(shù),如果區(qū)間 \(\min\) 值為 \(0\) 的話,此時(shí)出現(xiàn)次數(shù)就是 \(0\) 的出現(xiàn)次數(shù),那么正數(shù)位置數(shù)就是拿總長(zhǎng)度減去即可,如果區(qū)間 \(\min\) 值不為 \(0\),那就更好辦了,說(shuō)明整個(gè)區(qū)間都是正數(shù),直接返回總長(zhǎng)度。
未知渠道獲取的題
有 \(n\) 個(gè)矩形和 \(q\) 次詢(xún)問(wèn)。
每次詢(xún)問(wèn)給你一個(gè)矩形,問(wèn)這個(gè)矩形與 \(n\) 個(gè)矩形之并的交的面積。
原版要求強(qiáng)制在線,不過(guò)個(gè)人認(rèn)為沒(méi)有必要,離線就好。
要求單 \(\log\)。
承上啟下的好題了,給下面打個(gè)基礎(chǔ) qwq。
在 P5490 中,我們知道了怎么做矩形并,首先將矩形差分,然后線上維護(hù)「區(qū)間 \(\pm 1\),全局 \(0\) 的個(gè)數(shù)」。
然后這個(gè)題特別有意思,先形式化一下題意,此時(shí)查詢(xún)操作為「查詢(xún)?cè)儐?wèn)矩形內(nèi) \(\gt 0\) 的位置的個(gè)數(shù)」,考慮直接做不好辦,正難則反,數(shù)出「詢(xún)問(wèn)矩形內(nèi) \(0\) 的位置的個(gè)數(shù)」,然后拿詢(xún)問(wèn)矩形的面積一減就好了。
依舊考慮掃描線,由于查詢(xún)信息可差分,直接差分成 3-side 矩形,然后問(wèn)題變成了維護(hù)區(qū)間 \(0\) 的個(gè)數(shù)的歷史和,線段樹(shù)簡(jiǎn)單做就好。
那怎么強(qiáng)制在線呢,依舊是簡(jiǎn)單的,將掃描過(guò)程丟到主席樹(shù)上,此時(shí)可能需要一個(gè)標(biāo)記永久化,由于原題還需要離散化,所以需要還有一個(gè)垃圾分討……反正很惡心就對(duì)了/tuu。
注意,這個(gè)問(wèn)題還有一個(gè)對(duì)偶版本(應(yīng)該可以這么叫吧)是這樣的:
有 \(n\) 個(gè)矩形和 \(q\) 次詢(xún)問(wèn)。
每次詢(xún)問(wèn)給你一個(gè)矩形,問(wèn)這個(gè)矩形與 \(n\) 個(gè)矩形的并的面積。
兩者本質(zhì)上等價(jià),只不過(guò)一個(gè)是需要拿詢(xún)問(wèn)矩形的面積減一下,一個(gè)直接就是答案,如果哪場(chǎng)模擬賽出了這個(gè)問(wèn)題,可以直接爆錘出題人 /cf
可供練習(xí)的題:
- P5070 [Ynoi2015] 即便看不到未來(lái)
- CF799F Beautiful fountains rows
區(qū)間子區(qū)間問(wèn)題
給你一個(gè)序列,每次詢(xún)問(wèn)區(qū)間有多少個(gè)子區(qū)間滿足某個(gè)條件。
遇到這類(lèi)問(wèn)題,通常以?xún)奢S都為序列維建系(橫軸為 \(l\),縱軸為 \(r\)),把區(qū)間轉(zhuǎn)化為二維平面上的點(diǎn) \((l,r)\),把詢(xún)問(wèn)區(qū)間轉(zhuǎn)化為矩形。
不過(guò)由于區(qū)間有個(gè)性質(zhì)是 \(l\le r\),在平面上反饋過(guò)來(lái)的就是只有一個(gè)半平面才有貢獻(xiàn),所以我們所說(shuō)的「矩形」具體的講應(yīng)該是一個(gè)「三角形」的樣子,不過(guò)我們還是可以把它看成 2-side 矩形。
問(wèn)題就被轉(zhuǎn)化為查詢(xún) \(y\) 軸上的一個(gè)區(qū)間從 \(x = 1\) 到 \(x = now\) 這段時(shí)間中,總共有多少位置滿足條件,使用一個(gè)能夠維護(hù)歷史信息的數(shù)據(jù)結(jié)構(gòu)維護(hù)即可。
如果剛開(kāi)始不能理解的話(像我一樣),可以嘗試這樣理解:
我們對(duì) \(y\) 軸上的每個(gè)位置 \(i\) 維護(hù)當(dāng)前是否合法的值 \(a_i\)。
然后再給每個(gè)位置 \(i\) 引入一個(gè)計(jì)數(shù)器 \(cnt_i\)。
每次掃描線向右側(cè)移動(dòng)的時(shí)候,就是在進(jìn)行全局 \(cnt_i \gets cnt_i +a_i\) 的操作。
P3246 [HNOI2016] 序列
一個(gè)比較經(jīng)典的問(wèn)題,如果看懂上面內(nèi)容的話就可以一眼秒了。
首先將區(qū)間轉(zhuǎn)化為平面上的點(diǎn),即建系,橫坐標(biāo)為 \(l\),縱坐標(biāo)為 \(r\),然后考慮每一個(gè)最小值支配的區(qū)域,假設(shè)當(dāng)前序列的最小值 \(a_{\min}\) 的位置為 \(x\),則能夠發(fā)現(xiàn)任何包含了 \(x\) 位置的區(qū)間其貢獻(xiàn)都是這個(gè)數(shù),對(duì)應(yīng)到平面上就是一個(gè)\(l \le x\) 且 \(r \ge x\) 的 2-side 矩形。
如果你笨一點(diǎn)的話,可以使用分治將所有矩形預(yù)處理出來(lái),如果你不想被別人說(shuō)笨蛋的話,也可以使用單調(diào)棧維護(hù)。
不難發(fā)現(xiàn)查詢(xún)是一個(gè) 4-side 矩形,考慮差分,將其變成 3-side 矩形,發(fā)現(xiàn)線上需要維護(hù)一個(gè)「區(qū)間覆蓋,區(qū)間查詢(xún)歷史版本和」這樣的操作,線段樹(shù)維護(hù)即可。
當(dāng)然這個(gè)問(wèn)題存在更牛逼的「 強(qiáng)制在線 + \(O(1)\) 查詢(xún)」,且也是一個(gè)挺好的 trick,不出意外的話以后會(huì)聊到。
時(shí)間復(fù)雜度 \(O((n + q)\log n)\)。
CF997E Good Subsegments
(被析合樹(shù)橄欖了)
轉(zhuǎn)化一下題意先:
給定一個(gè)長(zhǎng)度為 \(n\) 的排列 \(P\),每次查詢(xún)區(qū)間 \([l,r]\) 中有多少子區(qū)間 \([l',r']\) 滿足 \(\max_{i=l'}^{r'}p_i-\min_{i=l'}^{r'}p_i = r' - l'\)。
考慮套用上面寫(xiě)的套路,將每個(gè)區(qū)間表示為二維平面上的點(diǎn) \((l',r')\)。
首先初始化 \((l',r')\) 的權(quán)值為 \(r' - l'\),顯然,對(duì) \(l \in [1,n]\) 做矩形減,對(duì) \(r\in [1,n]\) 做矩形加。
呆一點(diǎn)的,在序列上進(jìn)行最值分治(你用單調(diào)棧也行),具體地說(shuō),每次分治的序列中的最大/最小值都是分支中心,然后 \(\max\) 進(jìn)行矩形加,\(\min\) 進(jìn)行矩形減。
此時(shí),問(wèn)題被轉(zhuǎn)化為給定一個(gè)平面,進(jìn)行 \(O(n)\) 次矩形加減,問(wèn)一個(gè)矩形內(nèi)部有多少 \(0\)。根據(jù)前面的推論,顯然詢(xún)問(wèn)矩形是一個(gè) 2-side 矩形。然后使用掃描線和線段樹(shù)沿著橫/縱軸掃描整個(gè)平面,并把前面的矩形加減拆成線上序列加減(這個(gè)算平凡的)。
由于矩形內(nèi)元素一定非負(fù),所以套用「矩形面積并」時(shí)的線段樹(shù)維護(hù)套路,即可計(jì)算 \(0\) 的個(gè)數(shù)。
問(wèn)題來(lái)了,我們?cè)趻呙杈€后的 2-side 矩形詢(xún)問(wèn),需要查詢(xún)一個(gè)區(qū)間在前綴時(shí)間中 \(0\) 的個(gè)數(shù),這個(gè)線段樹(shù)打個(gè)歷史標(biāo)記也可以做,不過(guò)需要細(xì)細(xì)的討論一下,注意細(xì)節(jié)。
時(shí)間復(fù)雜度 \(O((n+m)\log n)\)。
EC final 2020 G. Prof. Pang's sequence
問(wèn)題轉(zhuǎn)化:
區(qū)間有多少子區(qū)間顏色數(shù)為奇數(shù) \(\iff\) 區(qū)間每個(gè)子區(qū)間顏色數(shù)\(\bmod 2\) 的和。
依舊是考慮將區(qū)間轉(zhuǎn)化為平面上的點(diǎn),建系,讓橫軸為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(r\)),縱軸也為序列維(對(duì)應(yīng)詢(xún)問(wèn) \(l\)),掃描線在橫軸上跑。
考慮掃描的過(guò)程中遇到一個(gè)值 \(a_r\),考慮找到 \(a_r\) 的前驅(qū) \(a_p\),則對(duì)于左端點(diǎn)在 \([p+1, r]\) 的所有區(qū)間來(lái)說(shuō),\(a_r\) 是一種新的顏色,否則不應(yīng)發(fā)生變化。
現(xiàn)在問(wèn)題就簡(jiǎn)單了,由于我們線上的每個(gè)位置只會(huì)是 \(0/1\) 的取值,所以我們?cè)趻呙杈€上維護(hù)一個(gè)「區(qū)間取反,區(qū)間和」這樣的一個(gè)東西,查詢(xún)就變成每依次區(qū)間查詢(xún),然后貢獻(xiàn)給對(duì)應(yīng)的查詢(xún)矩形。
不過(guò)這樣查詢(xún)顯然是要死掉的,所以差分一下,就變成了查詢(xún)區(qū)間歷史和,仿照上一個(gè)題用線段樹(shù)簡(jiǎn)單維護(hù)即可。
時(shí)間復(fù)雜度 \(O((n+q)\log n)\)。
P8421 [THUPC2022 決賽] rsraogps / P9335 [Ynoi2001] 雪に咲く花
全新的視角。
離線做掃描線,將詢(xún)問(wèn)變成矩形,由于詢(xún)問(wèn)信息可差分,直接在每個(gè)位置 \(i\) 上維護(hù)詢(xún)問(wèn)信息的前綴和 \(s_i\),詢(xún)問(wèn)答案減一下就好了。
現(xiàn)在考慮怎么維護(hù)這個(gè)前綴和,考慮掃描線向右移動(dòng) \(1\),如果這個(gè)時(shí)候 \(A_i,B_i,C_i\) 均未發(fā)生變化,那么 \(s_i\) 的變化量肯定與上次移動(dòng)保持一致,于是不難想到將 \(s_i\) 變成一個(gè)一次函數(shù)的形式 \(k_ix+b_i\),現(xiàn)在要修改的就只剩下 \(A_i,B_i,C_i\) 發(fā)生變化的部分,不難發(fā)現(xiàn) \(A_i,B_i,C_i\) 只會(huì)發(fā)生 \(O(\log V)\) 次變化,總共發(fā)生 \(O(n\log V)\) 次變化,所以直接暴力修改就好。
時(shí)間復(fù)雜度 \(O(n \log V + m)\)。
可供練習(xí)的題:
- P8868 [NOIP2022] 比賽
- P9747 [KDOI-06-S] 簽到題
對(duì)一維分治
有些時(shí)候,我們所維護(hù)的信息不能差分,這個(gè)時(shí)候就需要祭出我們的分治。
考慮對(duì)一維度分治后,此時(shí)將一個(gè) 4-side 矩形變成兩個(gè) 3-side 矩形(考慮從中間劈開(kāi),然后變成兩個(gè)開(kāi)口相對(duì)的 3-side 矩形),跑兩邊掃描線,就可以做到只有插入沒(méi)有刪除了。
由于分治每一次處理所有跨過(guò)分支中心的矩形,然后在向下繼續(xù)分治,所以復(fù)雜度會(huì)多一個(gè) \(\log\),復(fù)雜度證明請(qǐng)類(lèi)比歸并排序。
P1609 [Ynoi2009] rprmq1
由于這個(gè)題實(shí)在是太毒瘤了,這里就不放題解了,大家自己做做就好 /dk
二維掃描線
我們發(fā)現(xiàn),一維掃描線是對(duì)某個(gè)維度排序,每次查詢(xún) \([1,l]\) 的信息,然后沿著從 \(1\) 到 \(n\) 的路徑掃描全部的詢(xún)問(wèn)。
那啥是二維掃描線呢?套用一維掃描線的行為,我們嘗試得到二維掃描線的行為:
每次詢(xún)問(wèn) \([l,r]\) 的信息,以一種方式排序,使得經(jīng)過(guò)所有詢(xún)問(wèn),并且復(fù)雜度可接受。
實(shí)際上就是莫隊(duì),這個(gè)可以看看 Alex_Wei 的莫隊(duì)學(xué)習(xí)筆記,這里不多贅述。
*度
考慮一維掃描線 \(+\) 一維數(shù)據(jù)結(jié)構(gòu)可以處理二維靜態(tài)問(wèn)題。
然而實(shí)際上普通的維護(hù)序列問(wèn)題實(shí)際上也屬于兩個(gè)維度。
因?yàn)槭前凑战o定的操作序列操作的,這里不乏出現(xiàn)時(shí)間的維度。
所以每一次操作都可以看做是一個(gè) 3-side 矩形,然后時(shí)間一維,序列一維,在時(shí)間維上做掃描線,就將二維靜態(tài)問(wèn)題轉(zhuǎn)化為了一維動(dòng)態(tài)問(wèn)題。
于是這啟發(fā)我們:
掃描線序列維,數(shù)據(jù)結(jié)構(gòu)時(shí)間維
由于序列上的動(dòng)態(tài)操作可以看做成是序列一維,時(shí)間一維。
所以在有些情況下我們直接沿著時(shí)間維度跑不好處理,這個(gè)時(shí)候我們就可以考慮在序列維度上做掃描線,然后線上維護(hù)時(shí)間維。
這種問(wèn)題一般都是在線上查詢(xún)一個(gè)單點(diǎn)的信息,當(dāng)然,形勢(shì)好的話查詢(xún)區(qū)間信息也是可以的。
Comet OJ - Contest #14 D / P8512 [Ynoi Easy Round 2021] TEST_152
直觀的看這個(gè)題似乎特別不好處理。
不妨從貢獻(xiàn)的角度看這個(gè)題,把「經(jīng)過(guò)操作序列上 \([x,y]\) 的操作后形成的最終序列上每個(gè)位置的和」給拆成「每個(gè)時(shí)間點(diǎn)對(duì)最終序列的貢獻(xiàn)的和」。
這會(huì)產(chǎn)生兩個(gè)性質(zhì):
- 某個(gè)時(shí)間產(chǎn)生的貢獻(xiàn)會(huì)隨著時(shí)間的流逝逐漸消失,換句話說(shuō),時(shí)間點(diǎn) \(i\) 產(chǎn)生的貢獻(xiàn)是不增的。
- 時(shí)間點(diǎn) \(i\) 的貢獻(xiàn)是無(wú)后效性的,他不會(huì)受到前面的時(shí)間的影響,他只會(huì)被后面的時(shí)間所影響,這樣就意味著執(zhí)行了 \([1,y]\) 操作和執(zhí)行了 \([x,y]\) 操作后,時(shí)間點(diǎn) \(x\) 的貢獻(xiàn)一致。
現(xiàn)在容易想到建系,橫軸為操作序列維,縱軸為時(shí)間維,然后輔助維護(hù)一個(gè)執(zhí)行完 \([1,y]\) 操作的 \(c\) 數(shù)組。
考慮掃描線在橫軸上跑,線上維護(hù)每個(gè)時(shí)間點(diǎn)對(duì)當(dāng)前 \(c\) 數(shù)組的貢獻(xiàn),然后查詢(xún)就是線上的一個(gè)區(qū)間和。
現(xiàn)在考慮掃描線右移會(huì)發(fā)生什么,假使遇到了操作 \((l',r',v')\),則此時(shí)對(duì) \(c\) 數(shù)組進(jìn)行操作就是 \(c_i\gets v'\quad(i\in [l',r'])\),在增量的視角中就是 \(c_i\) 增加了 \(v'-c_i\),然后在線上減少那部分時(shí)間點(diǎn)的貢獻(xiàn),最后增加當(dāng)前時(shí)間點(diǎn)的貢獻(xiàn)。顯然,線上一個(gè)樹(shù)狀數(shù)組就夠了。
由于在我們給 \(c\) 數(shù)組賦值的時(shí)候是一個(gè)顏色段均攤,所以時(shí)間復(fù)雜度還是 \(O((n+m)\log n)\)。
P5524 [Ynoi2012] NOIP2015 充滿了希望
感覺(jué)正著掃做操作 \(1\)、\(2\) 可能是個(gè)天坑,于是考慮倒著做掃描線,也就是固定左端點(diǎn),*右端點(diǎn)。
考慮一個(gè)操作 \(3\) 在前面被一個(gè)操作 \(2\) 覆蓋了,則左邊面的所有操作對(duì)當(dāng)前操作來(lái)說(shuō)都無(wú)效了,也就是每個(gè)操作 \(3\) 只會(huì)被覆蓋一次。
再考慮操作 \(1\) 對(duì)操作 \(3\) 的影響,發(fā)現(xiàn)如果操作 \(3\) 已經(jīng)被操作 \(2\) 覆蓋了,則此時(shí)的操作 \(1\) 是無(wú)用的;如果沒(méi)被覆蓋,則說(shuō)明在未來(lái)我們的 \(x,y\) 的意義是反著的,此時(shí)若有操作 \(2\) 覆蓋了 \(y\) 等于在操作 \(1\) 后面覆蓋了 \(x\),這一個(gè)性質(zhì)啟發(fā)了我們遇到操作 \(1\) 時(shí)應(yīng)該直接交換,這樣才不影響正確性。
然后站在操作 \(2\) 的角度上觀察,發(fā)現(xiàn)有可能一次操作 \(2\) 會(huì)在同一個(gè)位置覆蓋掉多個(gè)操作 \(3\),這啟發(fā)我們用 vector 維護(hù)每個(gè)位置的操作 \(3\)。
更加深入的,發(fā)現(xiàn)交換操作等價(jià)于「兩次單點(diǎn)染色」,發(fā)現(xiàn)待覆蓋的區(qū)域和一次覆蓋的區(qū)域可以顏色端均攤,用 ODT 維護(hù)這個(gè)顏色端均攤就好。
綜上,不難得出掃描線左移是我們需要干什么:
- 遇到操作 \(1\) 時(shí):直接交換兩個(gè)位置的
vector,ODT 上交換兩個(gè)位置的顏色。 - 遇到操作 \(2\) 時(shí):ODT 上區(qū)間染色 \(1\),然后對(duì)每一個(gè)顏色為 \(0\) 的位置依次處理完詢(xún)問(wèn),并清空
vector。 - 遇到操作 \(3\) 時(shí):ODT 單點(diǎn)染色 \(0\),然后將當(dāng)前操作編號(hào)壓入對(duì)應(yīng)位置的
vector中。
每次查詢(xún)的時(shí)候就是一個(gè)區(qū)間和,直接樹(shù)狀數(shù)組維護(hù)就好,時(shí)間復(fù)雜度 \(O((m+q)\log n)\)。
P7560 [JOISC 2021 Day1] フードコート
建系,橫軸為序列維,縱軸為時(shí)間維,掃描線在橫軸上跑。
此時(shí)操作序列中的修改變成了橫著的線,詢(xún)問(wèn)變成了豎著的線,橫向差分一下,就變成了「單點(diǎn)修改、區(qū)間查詢(xún)」。
思考如何做查詢(xún)(也就是做操作 \(3\)),假設(shè)我們是在 \(i\) 時(shí)刻做的操作 \(3\),那么只需要找到 \(i\) 之前最近一次隊(duì)列空的時(shí)候 \(t\),在時(shí)間 \([t, i]\) 之間的每一次 pop 必然都是有效的,否則的話中間必然還要再空一次,不滿足「最近」這一個(gè)條件,這樣查詢(xún)操作就變成了在這一個(gè)區(qū)間里面二分了。
考慮如何刻畫(huà)這個(gè)最近一次隊(duì)列空,設(shè) push \(t\) 個(gè)元素為單點(diǎn) \(+t\),pop \(t\) 個(gè)元素為單點(diǎn) \(-t\),此時(shí)距離 \(i\) 時(shí)刻最近一次隊(duì)列空的時(shí)刻就是 \([1,i]\) 中最靠右的最大后綴和的位置,證明如下:
先證明此時(shí)空了:由于我們是最大后綴和,所以這之前必定是空的,否則我們就可以通過(guò)左端點(diǎn)向右擴(kuò)張來(lái)增大我們的后綴和。
在證明此后不空:假設(shè) \(x\) 時(shí)刻是最大后綴和左端點(diǎn), \(y\) 時(shí)刻隊(duì)列空了,\(x \lt y\),那么必然出現(xiàn)了 \([x,y]\) 的和 \(\lt 0\),我們的最大后綴和一定不會(huì)去選擇一個(gè)負(fù)數(shù)的前綴,所以該情況不可能出現(xiàn)。
由于會(huì)出現(xiàn)單點(diǎn) \(-t\) 這種牛魔東西不好二分,所以不如直接維護(hù)后綴和,這時(shí)候單點(diǎn) \(\pm t\) 變成前綴 \(\pm t\),然后查詢(xún)的時(shí)候注意別忘記消減掉后面時(shí)間帶來(lái)的影響。如果此時(shí)我們查出來(lái)的 \(\lt k\),那么可以直接跑路了,因?yàn)榇藭r(shí)隊(duì)列必定沒(méi)有 \(k\) 個(gè)元素。
現(xiàn)在我們已經(jīng)找到了最近一次隊(duì)列空的時(shí)間 \(t\),思考如何在 \([t,i]\) 上二分,首先查出 \([t,i]\) 中的 pop 量,然后給這個(gè) pop 量加上 \(k\),表示要找第 \(k\) 個(gè)位置,在 \([t,i]\) 上找到第一次 \(\ge\) pop 量的位置,然后讀取這個(gè)位置的屬性,即為答案,時(shí)間復(fù)雜度 \(O((n + q)\log n)\)。
當(dāng)然,如果你不是很會(huì)怎么單 \(\log\) 在線段樹(shù)的一個(gè)區(qū)間上二分,你可以像我一樣來(lái)一個(gè)笨的方法,就是先區(qū)間查出 \([1, t-1]\) 位置上的
push和,然后把他丟進(jìn)pop量里就可以了。
別忘了還原消減操作!!!
P8518 [IOI2021] 分糖果
建系,橫軸為序列維,縱軸為時(shí)間維,套用 P7560 的套路,只要我們找到了最后一次觸碰上界或者下界的時(shí)刻 \(t\),在 \(t\) 時(shí)刻之后的部分就變成了一個(gè)簡(jiǎn)單的求和,或者找到一個(gè)刻畫(huà) \(t\) 時(shí)刻之后部分貢獻(xiàn)的方法。
那么目前亟待解決的問(wèn)題就是如何描述「最后一次觸碰上/下界」這一個(gè)東西。
設(shè)當(dāng)前正在被操作的原序列 \(A\) 位置為 \(j\),時(shí)間序列為 \(time\),定義「制裁」為「當(dāng)前操作結(jié)束后觸碰到上/下界時(shí),對(duì)\(a_j\) 取 \(\min\) 或 \(\max\) 」的行為,定義「觸碰」為「當(dāng)前操作結(jié)束后,位置 \(a_j \gt c_j\) 或 \(a_j \lt 0\)」。
先忽略上界,考慮什么時(shí)候會(huì)碰到下界,不難發(fā)現(xiàn)在時(shí)間序列的最小前綴和 \([1,x]\) 中的 \(x\) 上,證明如下:
假設(shè) \(h\) 時(shí)刻被制裁,那么如果 \(t\) 時(shí)刻還想被制裁,則必須要保證時(shí)間序列上 \(t\) 位置上的數(shù) \(<\) \(-\max(0, \sum_{i=h+1}^{t-1} time_i)\),否則一定不會(huì)被制裁。
這就意味著如果你選上了一段和為非負(fù)數(shù)的區(qū)間,那么必然要選其后面的那一個(gè)被制裁的地方,此時(shí)才能保證當(dāng)前前綴和更小,否則一定不會(huì)選那一段區(qū)間。
現(xiàn)在考慮加上上界,不難發(fā)現(xiàn)下面這個(gè)性質(zhì):
- 若 \(i\) 時(shí)刻觸碰上界,\(j\) 時(shí)刻觸碰下界,則時(shí)間序列上 \([i,j]\) 區(qū)間的和一定 \(\lt -c\)。
- 若 \(i\) 時(shí)刻觸碰下界,\(j\) 時(shí)刻觸碰上界,則時(shí)間序列上 \([i,j]\) 區(qū)間的和一定 \(\gt c\)。
綜上,得「相鄰的上下界觸碰事件」之間區(qū)間的和的絕對(duì)值一定 \(\gt c\)。
現(xiàn)在我們的「最后一次觸碰上/下界」一定在最靠左滿足這一條件的區(qū)間的左端點(diǎn)或者右端點(diǎn),然而左端點(diǎn)只會(huì)有一種情況(就是有可能在觸碰下界后一段或觸碰上界后一段的區(qū)間和的絕對(duì)值 \(\gt c\)),對(duì)于這種情況我們?nèi)∫幌庐?dāng)前序列的前綴最大/小值就好了。
最后就剩怎么找我們所需要的那個(gè)區(qū)間,簡(jiǎn)單的,考慮線段樹(shù)維護(hù)「前綴最小和、前綴最大和、區(qū)間和」,然后絕對(duì)值最大子段和就是「前綴最大和 \(-\) 前綴最小和」,修改是單點(diǎn)修改,查詢(xún)時(shí)在線段樹(shù)上二分即可。
時(shí)間復(fù)雜度 \(O((n+q)\log q)\)。
UOJ 515. 【UR #19】前進(jìn)四
你會(huì)發(fā)現(xiàn)這個(gè)東西長(zhǎng)得很像我們的樓房重建,那么自然不難想到兩個(gè) \(\log\) 的做法,不過(guò)這里的期望時(shí)間復(fù)雜度為單 \(\log\)。
考慮離線,建系,設(shè)橫軸為序列維,縱軸為時(shí)間維,考慮倒著在序列維上做掃描線,線上維護(hù)時(shí)間。
轉(zhuǎn)換角度觀察這個(gè)問(wèn)題,發(fā)現(xiàn)每一次單點(diǎn)修改都影響了從「本次修改」到「下一次修改」的整個(gè)時(shí)間,故在線上維護(hù)取 \(\min\) 修改,然后發(fā)現(xiàn)查詢(xún)等價(jià)于查詢(xún)截止到這個(gè)時(shí)間,一共發(fā)生了多少次成功的取 \(\min\) 操作,換句話說(shuō),就是截止到當(dāng)前時(shí)間、當(dāng)前位置的后綴最小值一共變了幾次。
上面這一車(chē)東西考慮做「Segment tree beats」,時(shí)間復(fù)雜度 \(O((n+Q)\log n)\)。
計(jì)算幾何中的掃描線
回歸掃描線的本質(zhì),實(shí)際上就是拿一條線去掃平面來(lái)獲得所需要的信息。
因此較為顯然的,我們也可以將掃描線用在計(jì)算幾何中。
通常的套路是維護(hù)掃描線與幾何圖形截點(diǎn)的位置,與幾何圖形下一個(gè)拐點(diǎn)的位置。
由于我的計(jì)算幾何很菜,所以只能說(shuō)兩個(gè)比較簡(jiǎn)單的題目。
交點(diǎn)檢測(cè)
給定平面上 \(n\) 條線段,輸出所有交點(diǎn)的位置。
保證輸出量 \(\le 10^5\)。
掃描線掃描 \(x\) 軸,線上維護(hù)縱軸,也就是每個(gè)線段與掃描線的交點(diǎn)。
這樣有了一個(gè)較好的性質(zhì),就是如果線段 \(l\) 要與其他線段產(chǎn)生交點(diǎn),必定先與在掃描線上的相鄰線段有交點(diǎn)。
那么此時(shí)維護(hù)掃描線上相鄰線段什么時(shí)候發(fā)生交點(diǎn),計(jì)算交點(diǎn)并在掃描線上交換兩個(gè)線段的位置。
由于是線段,所以還需要維護(hù)線段的加入事件和刪除事件。
綜上,用平衡樹(shù)維護(hù)一下上述操作就好了,設(shè)交點(diǎn)數(shù)為 \(o\),則時(shí)間復(fù)雜度為 \(O((n + o)\log n)\)。
平面圖點(diǎn)定位
給定平面上 \(n\) 條直線,將平面劃分為了一個(gè)平面圖
你需要建出這個(gè)平面圖。
類(lèi)比交點(diǎn)檢測(cè),發(fā)現(xiàn)如果出現(xiàn)了交點(diǎn),則說(shuō)明這個(gè)的一個(gè)區(qū)域被封閉了。
這樣一直做就可以找到所有區(qū)域以及相鄰區(qū)域。
設(shè)區(qū)域數(shù)為 \(o\),則時(shí)間復(fù)雜度為 \(O((n + o)\log n)\)。
可供練習(xí)的題:
- P3268 [JLOI2016]圓的異或并
- P5525 [Ynoi2012] WC2016 充滿了失望
- P6106 [Ynoi2010] Self Adjusting Top Tree
樹(shù)上掃描線
考慮在序列上的時(shí)候我們的詢(xún)問(wèn)會(huì)轉(zhuǎn)化為 \(O(1)\) 個(gè)矩形。
現(xiàn)在問(wèn)題上樹(shù),不難想到將樹(shù)上問(wèn)題轉(zhuǎn)化為序列上的問(wèn)題,故進(jìn)行樹(shù)鏈剖分。
這里利用了一個(gè)樹(shù)鏈剖分的一個(gè)非常優(yōu)秀的性質(zhì):「樹(shù)上的任意一條路徑劃分成不超過(guò) \(O(\log n)\) 條連續(xù)的鏈」。
由于這一個(gè)性質(zhì),我們將拆分出來(lái)的鏈分別做矩形,此時(shí)最多會(huì)出現(xiàn) \(O(\log n)\) 個(gè)矩形。
如果考慮路徑上點(diǎn)之間的關(guān)系的話,則最多會(huì)出現(xiàn) \(O(\log^2 n)\)。
然后我們就在原先復(fù)雜度基礎(chǔ)上多了一個(gè)或者兩個(gè) \(\log\),復(fù)雜度在大部分情況下依舊是可以接受的。
未知渠道獲取的題
定義一個(gè)點(diǎn)對(duì) \((i,j)(i \not= j)\) 是好的當(dāng)且僅當(dāng):
\[\exists k \in N^{*},\qquad \lfloor\dfrac{i}{k}\rfloor = j \]給定一棵 \(n\) 個(gè)點(diǎn)的樹(shù),樹(shù)上點(diǎn)的權(quán)值互不相同,點(diǎn)權(quán)的值域?yàn)?\(m\),共有 \(q\) 次詢(xún)問(wèn)。
每次詢(xún)問(wèn)給定一個(gè)路徑 \((x,y)\),問(wèn)路徑上多少個(gè)點(diǎn)對(duì) \((u,v)\) 滿足 \((a_u, a_v)\) 是好的。
\(1 \le n,m,q \le 5\times 10^4\),時(shí)限 \(2.5\) 秒。
考慮整除分塊,預(yù)處理出所有符合條件的點(diǎn)對(duì)。
建系,設(shè)橫縱坐標(biāo)都為序列維(dfn 維),則點(diǎn)對(duì)變成平面上的點(diǎn)。
對(duì)于每個(gè)路徑,預(yù)處理所有可能的詢(xún)問(wèn)矩形,即兩個(gè)鏈組合在一起組成一個(gè)詢(xún)問(wèn)矩形,由于樹(shù)鏈剖分的性質(zhì),不難發(fā)現(xiàn)矩形數(shù)是 \(O(q\log^2 n)\) 的。
然后現(xiàn)在的問(wèn)題就變成一個(gè)簡(jiǎn)單的二維數(shù)點(diǎn),時(shí)間復(fù)雜度 \(O(q \log ^ 3 n + n \sqrt m \log n)\),足以通過(guò)該題,由于常數(shù)極小,把標(biāo)算給錘了。
可供練習(xí)的題:
- P1600 [NOIP2016 提高組] 天天愛(ài)跑步
總結(jié)
- 上一篇: 临终前不将遗产留给儿子,却托付给了胡歌,
- 下一篇: c# char unsigned_dll