按照思维难度加大和代码难度减小的顺序,我们来看这道题的不同做法。

若你无畏,我亦无畏 - 平衡树

平衡树简直是天然用来维护这种操作的——合并两个区间,提取一个值。我们可以对每个行的前 \(m-1\) 位和最后一列各维护一棵平衡树。平衡树上二分得到要删除的数,将当前区间分成 左边 - 要提走的数 - 右边。然后合并左边和右边,在末尾加上会新来的数。

但是这里存在一些问题,也就是我们不能真的给每一行开大小为 \(m-1\) 的平衡树,这怎么办呢?

  • 策略一:动态开点。我们一开始只插入一个大小为 \(m-1\) 的节点代表整个一开始的平衡树,当它的左右儿子存在但是不真正插入。一旦查询到,再把左儿子和右儿子插入进去,再往下遍历。因为我们最多访问 \(q\) 次,所以最多插入 \(O(q\log n)\) 个新点。

  • 策略二:断点分裂。我们在平衡树上维护区间,\([l,r]\) 的区间。同时记录它的大小和原始对应的 \(l\)。当我们查询到要的点是 \([l,r]\) 之间的第 \(x\) 个数时,就返回 \(l+x-1\)。然后删除的时候,将 \([l,r]\) 分裂成 \([l,x-1]\) 和 \([x+1,r]\)。我们发现,每个平衡树其实都是在所有被删除的点的间隔下被分成了若干个连续的区间。每次查询最多分裂出两个区间,点数是 \(O(n)\) 级别的(线性空间)。

  • 策略三:两个 \(Treap\)。我们在前面的过程中,都是实现了 \(n+1\) 棵同功能平衡树。我们想想两棵不同功能平衡树有什么好处——我们可以一个用来记录正常位置被删掉的点,一个用来记录所有新加入的点!那么,我们可以在所有被删除的点上同时维护紧跟着它的一段区间,在”删除 \(Treap\)”上二分找到答案。如果“删除 \(Treap\)”本身已经没有足够多的数了,就在新加入的点里面,也就是“插入 \(Treap\)”里面二分,得到答案。然后每次在“插入/删除 \(Treap\)”上增加一个点,在“插入 \(Treap\)”中增加一个点。

当然,以上的所有平衡树做法中,大部分都使用于所有的平衡树。不过因为是维护区间的缘故,当以 \(fhqTreap\) 为最佳选择。

转圜余地,权衡之策 - 线段树 (Pure)

我们可以使用 \(n+1\) 个动态开点线段树。在节点上记录当前行当前区间没有被删除的数的总数,在叶子节点记录当前点的数。每次在线段树上二分。最多插入 \(O(q\log n)\) 个新点。

在平衡树中存在的问题,在线段树中依然存在。不过因为动态开点线段树的天然性质,解决的方案要更加方便。

  • 策略一:动态权值。对于一个区间,它 \(m-1\) 以内的部分是有先天权值的,以外的部分是没有先天权值的。那么我们在线段树 \(newnode\) 的时候,将新的 \(sum\) 设定成在 \(m-1\) 以内的部分长度。也就是 \(\max\{\min\{r,m-1\}-l+1,0\}\)。这样我们就可以方便的进行二分和加点了。

  • 策略二:两种线段树。我们给每行开两种线段树,一种维护前 \(m-1\) 个数,一种维护新加入的所有数。第一种的先天权值是 \(r-l+1\),只需要支持动态开点删除。第二个负责在新点超出范围的时候查找,不仅删除还要插入。两种不同的先天权值区间用两个不同的线段树来概括,主要代码可以大段复制,只在 \(newnode\) 等为数不多的地方进行更改,免去了维护最后一列和其他长度不一样的烦恼。

  • 策略三:满赋溢出。我们发现,我们其实只需要维护前 \(m-1\) 个有值的位置,而插入其实是从尾部逐步插入。已经被加入的部分本来就应该是 \(1\),后面的部分在二分的时候根本不会被关注到,我们不如直接给所有的区间的先天权值都设为 \(1\)(因为溢出部分的权值对前 \(m-1\) 的查询没有影响)。我们也不需要支持插入了,只需要更改叶子节点的 \(value\) 即可。因为每次修改修改一条链或一个点,还可以使用特殊的方法建树得到更优秀的实现。

思维风暴,人力之伟 - 线段树 \(+\) 其他技巧

  • 策略一:提前处理。我们假设删除点的时候不是删除合并两边,而是保留原来的点。每次处理一行。也就是提前 \(O(n)\) 建出一棵不动态开点的线段树,对于每一行的所有询问,找到它对应原来的位置的哪个点(也就是如果不删除点,只是把点改成 \(0\),它对应 \(m-1+q\) 内第几个点),删掉这个点。整行查询完之后,将所有被删除的位置加回去。预处理对应位置的复杂度是 \(O(q\log n)\) 的。然后模拟部分就不再用到线段树。因为我们已经提前处理出了坐标,所以可以直接利用新的映射关系在 \(Append_x\) 序列中 \(O(1)\) 按照下标查询。至于删除就完全不需要处理了,因为我们维护的就是静态的序列。每次在 \(Append_n\) 里面加一个点即可。

  • 策略二:预后处理。和上面的做法反其道而行之。我们发现,每个询问的答案,要么依赖于前面删除了多少个数,要么依赖于后面加进来什么数。而前面删除了多少个数这种很好处理,对于后面加进来的数,我们可以不去管它加进来什么,而是找到那次修改它它的询问编号。我们对每个询问,处理出它的父亲——也就是它的答案依赖于哪个位置原先的答案。这一过程可以使用线段树,也就是在线段树上二分没有被删掉的位置。如果当前位置的值依赖于 \(append\) 进来的数,就把它的父亲挂上去。否则就是当前行列的答案。最后像并查集压缩路径一样找到每个点的答案。压缩直到当前询问不依赖于别的询问为止。

  • 小技巧:权值处理。我们发现值分两种,一种是原先表中的规律的值,一种是插入进来的新值。原先表中规律的值很好处理,但是新值有点麻烦,需要在线段树的每个点记录。这样就要给每个节点加一个新的信息,增加了规律值的浪费。我们可以用 \(Hash\),对于规律的值,直接按照公式给出答案。否则存进 \(Hash\) 表。每次直接往 \(Hash\) 表里查询,就可以优化一点空间的常数(动态开点,空间很重要)

NOIP2017 - D2T3 - phalanx的更多相关文章

  1. [luogu P3960] [noip2017 d2t3] 队列

    [luogu P3960] [noip2017 d2t3] 队列 题目描述 Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Syl ...

  2. 【学术篇】NOIP2017 d2t3 列队phalanx splay做法

    我可去他的吧.... ==============先胡扯些什么的分割线================== 一道NOIP题我调了一晚上...(其实是因为昨晚没有找到调试的好方法来的说...) 曾经我以 ...

  3. NOIP2017 D2T3列队

    这题我改了三天,考场上部分分暴力拿了50,考完试发现与正解很接近只是没写出来. 对于每一行和最后一列建n+1颗线段树,维护前缀和. 复杂度qlogn 假如你移动一个坐标为(x,y)的人,你要将第x行线 ...

  4. NOIP2017 D2T3 题解

    题面 这种数据范围不是乱搞dfs就是乱搞状压DP 首先应该通过任一方式求出a和b的值: 任意一条抛物线只用两头猪就可以确定,所以我们N^2枚举,并把在这两头猪的抛物线上的猪都存进状态state[i][ ...

  5. luogu 3960 列队

    noip2017 D2T3 列队 某zz选手当时直接放弃了写了50还写错了 题目大意: 有一个n行m列的方阵,第i行j列的点编号为(i-1)m+j 每次把第x行y列的点拿出来,然后把这一行它之后的点都 ...

  6. 【NOIP题解】NOIP2017 TG D2T3 列队

    列队,NOIP2017 TG D2T3. 树状数组经典题. 题目链接:洛谷. 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. ...

  7. NOIP2017列队(phalanx)解题报告

    列队作为NOIP2017最后一道题,其实并不难,只是相对于其它题目,有点小小的工业 首先,这道题我用splay维护的,如果你不会splay,又想学一下splay,可以来这里学一学,接下来步入正题 首先 ...

  8. [NOIP2017 TG D2T3]列队

    题目大意:有一个$n \times m$的方阵,第$i$行第$j$列的人的编号是$(i-1) \times m + j$. 现在有$q$个出列操作,每次让一个人出列,然后让这个人所在行向左看齐,再让最 ...

  9. [NOIP2017]列队 离线+SBT

    [NOIP2017]列队 题目描述 Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵 ...

  10. noip2017爆炸记——题解&总结&反省(普及组+提高组)

    相关链接: noip2018总结 noip2017是我见过的有史以来最坑爹的一场考试了. 今年北京市考点有一个是我们学校,我还恰好被分到了自己学校(还是自己天天上课的那个教室),于是我同时报了普及提高 ...

随机推荐

  1. 【Shell案例】【打印指定行用sed、for循环、head和tail配合使用】4、输出第5行的内容

    描述写一个 bash脚本以输出一个文本文件 nowcoder.txt 中第5行的内容. 示例:假设 nowcoder.txt 内容如下:welcometonowcoderthisisshellcode ...

  2. rate-limit 一款 java 开源渐进式分布式限流框架使用介绍

    项目简介 rate-limit 是一个为 java 设计的渐进式限流工具. 目的是为了深入学习和使用限流,后续将会持续迭代. 特性 渐进式实现 支持独立于 spring 使用 支持整合 spring ...

  3. java中的动态绑定机制

    本文主要讲述java中的动态绑定机制. 老韩ppt关于动态绑定机制: 示例代码如下: public class DynamicBinding { public static void main(Str ...

  4. IntelliJ IDEA中我最爱的10个快捷操作

    前言 欢迎关注微信公众号「JAVA旭阳」交流和学习 IntelliJ IDEA提供了一些Java的快捷键,同样也可以帮助我们提高日常的开发效率.关于这些快捷操作,你知道那几个呢? 1. psvm/ma ...

  5. 使用 GPG 签名提交

    GPG 签名是对代码提交者进行身份验证的一种补充,即证明代码提交来密钥持有者,理论上可以确保在目前的破译技术水平下无法篡改内容.您可以使用 GPG 工具 (GNU Privacy Guard) 生成密 ...

  6. HHKB Programming Contest 2022 Winter(AtCoder Beginner Contest 282)

    前言 好久没有打 AtCoder 了.有点手生.只拿到了 \(\operatorname{rk}1510\),应该上不了多少分. 只切了 \(\texttt{A,B,C,D}\) 四题. A - Ge ...

  7. 为什么要虚拟化,为什么要容器,为什么要Docker,为什么要K8S?

    前言 如标题中的问题所提到的虚拟化,容器,Docker和K8s那样,我们不妨这样问:这些技术到底适用于哪些场景,有没有别的技术可以替代?这些技术的优劣在哪里? 下面我将针对性地从以上几个问题的出发点, ...

  8. MySQL 表的创建、复制、修改与删除

    MySQL中如何利用代码完成表的创建.复制.修改和删除. 一.创建表 --创建新表,如果存在则覆盖 drop table [if exists] 表名; --创建新表,如果存在则返回 create t ...

  9. 代码小DEMO随笔---JS原生手机版本alert弹框

    之前的随笔写的是WEB版本的弹框,这次是手机版本,欢迎路过的大佬们提出更好的写法~~ <!DOCTYPE html> <html lang="en"> &l ...

  10. Angular8中共享模块,共享组件的写法(anular其他模块组件引用方法)

    Angular8中共享模块,共享组件的写法(anular其他模块组件引用方法) 第一步:全局创建一个共享的module 这里示例创建一个模块common-share 创建完成后,会生成两个文件 文件1 ...