2024集训D10总结
集训D10总结
模拟赛总结
没啥好说的 , lxl 场 , T2写太逆天了 .
T1
题意
\(10^8\) 边长平面上斜边斜率 \(-1\) 直角三角形 , 矩形加 , 矩形求和 .
题解
首先显然可以 \(CDQ\) 分治转化为一般扫描线 .
矩形加矩形求和是简单的 , 重点考虑三角形 . 其实也可做 , 直接分讨三角形和矩形交的形态 , 贡献是可以对常数项 , 一次项 , 二次项分别维护的 , 不同情况分别扫就可以做到 .
精细实现是困难的 , 也没有放精力来研究 , 记录一下大致思路 .
总结
这道题就是思考复杂程度比较大 , 但是基本的东西比较显然 .
考场上打完暴力就跑确实是最优解 .
T2
题意
维护函数序列 \(F_i: f(x)=max(x,a_i-x)\) , \(q\) 次询问 \(x\) 经过 \(F_l\cdots F_r\) 的结果 .
题解
这个是经典的函数复合问题 , 考虑用平衡树维护所有点值 , 每次统一处理 .
发现每次操作相当于把 \(\frac{a_i}{2}\) 的部分关于 \(\frac{a_i}{2}\) 翻折 , 考虑平衡树直接做有交集合并这件事情 . 到这里只能考虑分析均摊或者树套树维护 . 考场上构造了并不正确的hack数据 , 认为没有均摊性质 ...... 于是直接上树套树了 .
树套树 , 考虑直接离线维护点值不可取 , 于是维护区间的复合函数 , 维护方法就是用平衡树 , 每一个点表示一个方差为 \(1/-1\) 的段 .
考虑正向做会出现交集合并 , 于是反向做就是从函数中取出若干有交区间 , 不交地拼成新函数 . 这道题就是从中点后分出长为 \(len\) 一整段 , 和一个 \(V-len\) 段拼在左面 . 这可以用可持久化平衡树实现复制区间 .
然后在线段树上每一段暴力这样构造出整段的函数 , 操作次数是 \(O(n\log n)\) 的 , 时间 \(O(n\log ^2 n)\) . 问题就出在了空间也是 \(O(n\log ^2 n)\) 上 , 因为是任意拼接 , 为了保证正确性 , 必须在所有操作时复制点 , 空间常数也很大 .
有一些办法优化空间复杂度 :
- 善用分治 , 线段树上 \(len\le K\) 时考虑不构造平衡树 , 直接暴力重构 , 额外的复杂度极限就是 \(4\times K\) , 可以减少很多层上的平衡树处理 .
- 在保证当前为新点时不用再复制 . 比如 split , merge 操作时可以在 pushdown 时复制子节点 , 另外可以打 tag 记录这一点是否为新点 , 在查询之类操作时就没有必要重建了 .
- 尽可能少用 split , merge .
然而还是没有用 , 我甚至真的花了整场时间写出来了这个东西 , 然后空间爆完了 .
这样做还是有可取之处的 , 因为这可以做到在线 .
均摊分析 , 首先要想清楚一点 , 对于 FHQ treap 来说 , 启发式合并是十分愚蠢的 .
正确的合并方式 , 考虑 FHQ 可以按值域分裂 , 关键矛盾是值域有交 , 正确的合并方式是每次提出不交的一部分 , 合并后循环处理剩下的部分 , 这样这保留了不得不做的操作 .
相反 , 启发式合并不必要地破坏了原来良好的树结构 .
到这里 , 感性地分析 , 翻折操作相当于不断缩紧了值域 , 而合并时操作数显然取决于较稀疏的一部分 , 因此一组数据造成的复杂度是有一个较小的上界的 . 有这些在考场上其实也够了 .
更严谨的分析一下 , 考虑每次合并一个不交段 , 操作代价为 \(1\) , 发现势能是与疏密程度相关的 , 对于每一个点设势能为与左右点的距离和 , 考虑每次合并时的 , 属于原来一棵树的 , 两个端点 \(x,y\) , 中间至少插入了一个点 \(m\) , 则 $d(x,m) , d(m,y) $ 两者之间至少有一个减半 . 因此摊到每个点上 , 贡献的复杂度时 \(logW\) , 总复杂度就是 \(n\log n \log W\) .
总结
在考场上有时还是要相信直觉 !
不过这种一步之遥的问题 , 也确实值得关注 . 这次是分析复杂度把自己绕进去了 , 犯了"神秘化" 的问题 , 草草证伪了其实正确的做法 .
此外是对平衡树应用确实是经验不足 , 平时做题需要数据结构维护时大多选择了更有把握的线段树 , 树状数组 , 甚至 stl 等等 , 几乎不接触平衡树 , 哪怕需要平衡树也大多在用基本操作 , 专门的数据结构确实没怎么专门研究过 . 正好这次也算是在思考完全透彻的前提下 , 写了一次可持久化平衡树 , 平衡树暴力合并这些东西 .
T3
题意
给序列 \(a_1,\dots,a_n\) 和排列 \(b_1,\dots,b_n\),共有 \(m\) 次操作:
修改操作:给定 \(x,y\),将 \(a_x\) 改为 \(y\);
查询操作:给定 \(l,r,x\),查区间 \([l,r]\) 内最长的子区间 \([l',r']\)(即满足 \(l\le l'\le r'\le r\)),使得对 \(l'\le i<r'\) 有 \(a_{i+1}=b_{a_i}\),且存在 \(l'\le i\le r'\) 使得 \(a_i=x\)。需要输出满足条件的 \(r'-l'+1\) 的最大值,若不存在则输出 \(0\)。
题解
考虑第一个限制相当于把整个序列划分成若干段 . 因此每次单点修改最多影响到 \(O(1)\) 个段 , 段可以用 set
直接维护 .
考虑查询 , 考虑每次查询区间相当于左右两个散块 , 中间一些整块 , 因为散块的长度不定 , 与整块维护是不优的 , 可以先判断段内是否有 \(x\) 就可以 . 这样就只剩整段的问题了 .
考虑整段的贡献是确定的 , 即长度 \(len\) , 问题是确定所要的值 \(x\) 是否在当前段中 . 发现 \(x\) 应该单独作为一个值域维度来考虑 , 一个段相当于在序列的单点位置 , 值域上的若干个区间上有贡献 , 以下标为横坐标 , 值域为纵坐标 . 相当于一条竖线 .
查询答案是在值域一个点 , 序列一个区间求 \(\max\) , 相当于一条横线 .
考虑用二维数据结构维护 .
先处理一个棘手的问题 , 每个段的值域并不连续 , 但是因为基于置换环 , 一个合法段的值域一定是一个环的一部分 , 因此可以把每个环的值域对应到维护的值域的连续区间 , 这样每个段的值域区间不多于 \(2\) . 这样就变成单纯的加竖直线段了 .
考虑这个操作需要撤销 , 因此加竖线最好不完全用线段树维护 , 同时区间操作又最好由线段树维护 . 因此考虑线段树维护值域 , 内层平衡树维护下标 .
修改操作相当于外层区间修改 , 内层单点插入/删除 . 查询操作相当于外层单点查询 , 内层查询区间最大值 . 都是容易实现的 .
总结
总体而言 , 思维跨度并不十分大 , 基本上就是根据已有信息分析 , 转化 , 到考虑二维数据结构维护 . 但是实现十分困难 , 尤其是单点修改时的分讨规模很大 .
思路上的关键就是每次"自然" 的思路遇到瓶颈时 , 适当转化 , 尤其抓住主要矛盾 . 比如查询时处理连续整块贡献 , 后来的发现查询问题是是否包含 \(x\) , 因此考虑转到一个二维问题 , 发现可以直接维护 .
另外也有找性质处理转化的部分 , 就是通过置换环把不连续的值域转成连续的 .
写了一晚上没调过 , 程序实现难度还是有的 , 主要问题是除了比较板的平衡树 , 线段树 , 还要搞一堆讨论来处理单点修改时连续段的变化 . 总的来说大部分题实现还不至于难到这种程度 . 写一写这种题 (比如 WC2018通道 ) 也是有益处的 , 过大的码量和复杂程度强制要求思维要非常清晰 , 可以提升做实现简单的题时的效率 .
总结
因为是 lxl 场 , 然后恰好用前两天看到的毒瘤东西想出了 T2 就去冲 T2 了 , 当然空间卡不进去 (其实T3 正解也有类似问题) . 当然还是去打完了最基本的部分分就没有多分精力了 . 分数不是很好看但是也不算太差 .
总体而言 , 这种特殊的模拟赛更多的还是要吸取到一些东西 , 比如对平衡树合并的正确认识 , T3 也是好题 , 思维不算难 , 数据结构本身也没有那么毒瘤 (相比线段树套可持久化平衡树这种东西 ) , 不过看样子一晚上调不完了 , 可以留到后面完成 .
另外就是感觉自己在场上卡更多是一步之遥这种情况 . 即整体思路接近正解 , 但是缺少关键trick或者误判 , 这种情况很难避免 , 尤其大多是在精细实现时缺少考虑 , 或者是有纠结 . 保证整体思路很重要 , 不管是进一步延申某个思路 , 还是排除某个思路 , 不仅要果决 , 还要慎重 . 要区分好发散思维和集中研究的应用 , 否则就可能陷入反复纠结 , "神秘化"问题 .
另外要吸取一些赛时经验 : 场上纠结的问题尽可能着眼整体思维 , 构造反例确实是快速排除错误思路的好方法 , 但是也不能过于绝对 .