P3960

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

posted @ 2024-05-01 22:11  wscqwq  阅读(1)  评论(0编辑  收藏  举报