P3960

wscqwq發表於2024-05-01

P3960 [NOIP2017 提高組] 列隊

題意簡述

給定一個 \(n\times m\) 的網格,按照從上到下、從左到右的順序從1開始編號。有q次操作,每次操作刪去第x行y列的格子,需要你輸出這個格子的編號,然後這一行後面的格子往前平移填補空格,使得空位在第x行第m列。然後第m列的格子往上填補空缺,使得空位到達第n行第m列,刪去的那個格子填到這個位置。

\(n,m,q\le 3\times 10^5\)

思路

首先來考慮線性的情況。

我們需要

  1. 刪去一個位置上的數。
  2. 把這個數放到尾部。

對於2,使用一個 vector 即可。

對於1,不是真的把它刪除。考慮用線段樹維護整個序列的位置,每個節點記錄這個區間被刪除的數字個數 \(cnt\)。每次根據左兒子的size-cnt判斷在左半邊還是右半邊,找到位置更新即可。

找到是第幾個數,分類討論:

  1. \(1\sim m\),使用公式計算。
  2. 後面,在 vector 內查詢。

這個線段樹的長度需要達到 \(m+q\)

進入正題

發現每一行是獨立的,只跟最後一列有關。

我們用上面的方法維護每一行的線段樹(稱\(t_1\sim t_n\),對應 vector \(v_1\sim v_n\)),不過區別是在 \(1\sim m-1\),因為 \(m\) 包括在最後一列構成的線段樹中。(稱 \(t\),對應 vector \(v\)

討論:

  1. \(y=m\),此時在 \(t\) 中查詢第 \(x\) 個數,使用上面的分類討論方法得到編號,然後插入到 \(v\)
  2. \(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