P3960 [NOIP2017 提高組] 列隊
題意簡述
給定一個 \(n\times m\) 的網格,按照從上到下、從左到右的順序從1開始編號。有q次操作,每次操作刪去第x行y列的格子,需要你輸出這個格子的編號,然後這一行後面的格子往前平移填補空格,使得空位在第x行第m列。然後第m列的格子往上填補空缺,使得空位到達第n行第m列,刪去的那個格子填到這個位置。
\(n,m,q\le 3\times 10^5\)
思路
首先來考慮線性的情況。
我們需要
- 刪去一個位置上的數。
- 把這個數放到尾部。
對於2,使用一個 vector
即可。
對於1,不是真的把它刪除。考慮用線段樹維護整個序列的位置,每個節點記錄這個區間被刪除的數字個數 \(cnt\)。每次根據左兒子的size-cnt判斷在左半邊還是右半邊,找到位置更新即可。
找到是第幾個數,分類討論:
- 在 \(1\sim m\),使用公式計算。
- 後面,在
vector
內查詢。
這個線段樹的長度需要達到 \(m+q\)。
進入正題
發現每一行是獨立的,只跟最後一列有關。
我們用上面的方法維護每一行的線段樹(稱\(t_1\sim t_n\),對應 vector
\(v_1\sim v_n\)),不過區別是在 \(1\sim m-1\),因為 \(m\) 包括在最後一列構成的線段樹中。(稱 \(t\),對應 vector
\(v\))
討論:
- \(y=m\),此時在 \(t\) 中查詢第 \(x\) 個數,使用上面的分類討論方法得到編號,然後插入到 \(v\)。
- \(y\neq m\),還是要像 \(1\) 一樣,但是編號是要插入 \(v_x\)(第 \(m\) 列的現在變成 \(m-1\),而維護長度就是 \(m-1\),相當於插入了末尾了),在 \(t_x\) 中查詢第 \(y\) 個數,得到的編號要插入 \(v\)(因為它到 \((n,m)\) 去了)。
線段樹的大小:\(len=\max(n,m)+q\) 肯定夠用。
由於開不下 \(n+1\) 個線段樹,採用動態開點線段樹。
寫程式計算空間:\(len_{\max}=3\times 10^5+3\times 10^5=6\times 10^5\),寫一個遞迴計算線段樹長度為 \(6\times 10^5\) 時最大深度為 \(21\),考慮到一次最多涉及到 \(t_x,t\),故需要節點數為最大深度 \(\times\) 詢問次數 \(\times2\), \(21\times 3\times 10^5\times 2\) 個節點。
注意動態開點線段樹要用引用,有些地方開 long long
。
https://www.luogu.com.cn/record/157622900