简单数据结构做题记录

CF526F Pudding Monsters

典题,发现这本质上是一个一维问题,一个区间合法当且仅当 \(\max - \min = r - l\),枚举右端点维护左端点的变化量,用两个单调栈维护到 \(r\) 的最大最小,用线段树维护区间最小值及其个数,由于 \([r, r]\) 满足条件且 \(\max -\min - r + l\geq 0\),因此每次累加最小值个数即可。

时间复杂度 \(\mathcal O(n\log n)\)

CF603E Pastoral Oddities

先找“每个点度数均为奇数”的充要条件,容易发现是“加入所有边后不存在奇连通块”。

证明是简单的。

充分性:显然对于每个连通块独立,随便找一个点当根求出一棵生成树,自底向上考虑每个非根的点,若儿子度数为偶数则加上其与父亲的连边,否则删去这条边,此时除了根所在连通块都满足条件,同时注意到总度数为偶数,有奇数个点的度数为奇数,必然根的度数也为奇数。

必要性:若存在奇数连通块,则要求总度数为奇数,无论怎么删去边,总度数都为奇数,显然非法。### CF526F Pudding Monsters

典题,发现这本质上是一个一维问题,一个区间合法当且仅当 \(\max - \min = r - l\),枚举右端点维护左端点的变化量,用两个单调栈维护到 \(r\) 的最大最小,用线段树维护区间最小值及其个数,由于 \([r, r]\) 满足条件且 \(\max -\min - r + l\geq 0\),因此每次累加最小值个数即可。

时间复杂度 \(\mathcal O(n\log n)\)

CF603E Pastoral Oddities

先找“每个点度数均为奇数”的充要条件,容易发现是“加入所有边后不存在奇连通块”。

证明是简单的。

充分性:显然对于每个连通块独立,随便找一个点当根求出一棵生成树,自底向上考虑每个非根的点,若儿子度数为偶数则加上其与父亲的连边,否则删去这条边,此时除了根所在连通块都满足条件,同时注意到总度数为偶数,有奇数个点的度数为奇数,必然根的度数也为奇数。

必要性:若存在奇数连通块,则要求总度数为奇数,无论怎么删去边,总度数都为奇数,显然非法。

注意到能合并就合并是最优的,因为无论是偶 + 偶,奇 + 偶,奇 + 奇都不会增加奇数个数,于是对于单次询问,可以考虑类似 Kruskal 的过程得到答案。

注意到答案单调不升,可以考虑整体二分。

注意到每次加边的变化只有当两边子树都是奇数才有用,可以直接上 LCT。

注意到若我们从后往前处理,将边集排序后维护一个指针表示当前答案,对于已经加入答案的边而言我们需要在某一时间段维护其合法范围,就可以直接线段树分治维护。

时间复杂度 \(\mathcal O(n\log n \log m)\)

CF1446D2 Frequency Problem (Hard Version)

容易用调整法证明两个答案其中一个为全局众数,设为 \(maj\)

暴力就是再枚举另一个元素 \(x\),将 \(x\) 看做 1,\(maj\) 看做 -1,答案就是最长的和为 0 的段。

还有一个暴力:直接枚举出现次数 \(cnt\),双指针枚举 \(maj\) 的第一次出现位置,对 \([l, r]\) 的这些数开桶记录出现次数为 \(i\) 的有多少个,当 最大值恰为 \(cnt\) 且个数 \(\geq 2\) 则存在。

结合上面两个算法,考虑根号分治,对于 \(occ_i > B\) 的数直接 \(\mathcal O(n)\) 算,否则直接枚举 \(cnt\),也可以 \(\mathcal O(n)\) 算。

时间复杂度 \(\mathcal O(n\sqrt n)\)

CF997E Good Subsegments

显然就是 CF526F 的加强版。考虑记录 \(\max - \min - (r - l)\) 最小值,最小值个数,用历史线段树维护之。

每次将右端点扩展 1 时,需要对所有 \(t_i = 0\) 的位置 \(s_i \leftarrow s_i+1\),直接打一个只对最小值生效的 tag 即可。

CF319E Ping-Pong

注意到若两个区间相交但不包含,则二者双向可达。若一个大的包含一个小的,则只能从大区间到小区间。

不妨直接将第一类区间用并查集合并看做一个点,容易发现每次询问最多走一次大区间到小区间。对于一次从 \(x\) 走向 \(y\) 的询问,若 \(x ,y\) 已经在同一连通块或 \(x, y\) 为包含关系则可以到达。

接下来考虑如何合并只相交的线段,注意到插入的线段长度单调不降,考虑将一个线段 \([l, r]\) 加入 \([l+1, r - 1]\) 的集合中,每次查询 \(l\)\(r\) 中的元素合并,同时剩下的线段没有用,并清空集合变成合并后的大集合。

时间复杂度 \(\mathcal O(n\log n\alpha(n))\)

CF1163F Indecisive Taxi Fee

CF436F Banners

前面 \(\sum\limits_{b_i \geq c}w \times c\) 是平凡的,对于后面的 \(\sum\limits_{b_i < c, a_i \geq p}p\) 不妨扫描线,每次加入若干个 \(a_i\),对于 \(p\) 开线段树维护答案。则每次等价于将所有 \(p \leq a_i\) 的位置加上 \(y = x\) 的函数。问题变为:

维护一个初始全 0 的序列,支持对一个前缀加上一个一次函数,询问全局 $\max $。

很可惜李超树并不能维护一次函数相加后的 \(\min\),考虑分块,每块内部记录 \(tg\) 和初始值 \(a_i\),则 \(i\) 的贡献是 \(a_i + tg \times i\),根据经典结论将每个 \(i\) 看做 \((i, a_i)\),则只有上凸包中的点有用,由于每次斜率单减直接开指针扫,散块直接暴力重构。

时间复杂度 \(\mathcal O(n \sqrt n)\)

CF793F Julia the snail

先离线扫,将 \([l_i, r_i]\) 挂在 \(r_i\),用线段树对每个 \(l\) 维护初始在 \(l\),在经过所有不超过 \(r\) 的绳子能到达的最大高度,对于一个 \([l_i, r_i]\) 而言,等价于将所有 \(i \in [1, l_i], a_i \geq l_i, a_i \leftarrow \max(a_i, r_i)\)

由于 \(r\) 是单调枚举的,也可以看做 \(a_i \leftarrow r\),即区间 \(\geq\) 某值的赋值。

使用吉司机线段树(然鹅并不会势能分析) 维护。

CF1178G The Awesomest Vertex

显然 \(|\sum b|\) 是常数,问题变成每个点是 \(a|x|(a\geq 0)\),每次区间 \(x\) 加一个正数 \(v\),求区间最大。

显然不能 polylog,考虑分块,首先 \(|x| = \max(x, -x)\),维护 \(-x\)\(x\) 两次取 \(\max\) 即可。

此时问题变成 CF436E。

CF773E Blog Post Rating

手玩发现 F 一定是先单减后不降。

F 第一次不降的位置 \(i\) 满足 \(a_i \geq -i\),对于剩下的位置有 \(f_i = \min(f_{i - 1}+1, a_i)\)。答案是 \(f_n\)

用值域线段树维护所有已经加入的位置,每次线段树二分先找到第一个分界点 \(p\),在 \(p - 1\) 的答案是 \(-(p- 1)\)

\(f_n\) 的柿子拆开,\(ans = \min(a_n, a_{n - 1}+1, \dots, a_{p}+n - p, n - p+1 -(p - 1))\)

再维护 \(a_p - p\) 的区间 \(\min\),每次查询就变成了后缀 \(\min\)

时间复杂度 \(\mathcal O(n\log n)\)

CF765F Souvenirs

不妨设 \(j < i, a_i< a_j\),考虑哪些二元组 \((i, j)\) 可能成为最优决策:必然是单调递增的单调栈中所有 \(\geq a_i\)\(j\)。当然,不能直接枚举所有 \(j\),但发现若 \(k<j, a_k < a_j, a_j - a_k < a_k - a_i\),则该部分已经在 \((k, j)\) 算过了,所以合法的 \(k\) 必然满足 \(a_k - a_i<a_j - a_k \rightarrow a_k <\dfrac{a_i+a_j}2\)

出现了值域减半,因此总共的 \(k\) 最多有 \(n\log n\) 个,用一个线段树找一下即可,然后用树状数组维护一下二维数点。

CF679E Bear and Bad Powers of 42

先粗略估一下 \(a_i \leq qV\),而 \(42^{10} >10^{14}\),因此合法的 power 极少,直观上应该是一个势能分析。

考虑记录每个 \(a_i\) 与其最近的 \(42\) 幂次的距离,操作 3 相当于若区间最小值 = 0 则不断做一个区间减法,减完后若 \(<0\) 则暴力更新新的距离。则花费在每个数上的复杂度为 \(\mathcal O(\log_{42}qV \times \log n)\)

现在加上区间赋值,感觉用 ODT 复杂度摊着摊着就对了!但不能直接用 ODT 来表示连续段,因为这会使线段树区间发生变化。

考虑直接在线段树上区间赋值,这等价于将每个连续段再拆成了 \(\log n\) 个段,此时再考察操作 3:

  • 若线段树区间存在赋值标记,更新赋值标记重新计算距离。
  • 否则不断递归直到区间最小值 \(\geq x\) 或者到达叶子节点。

时间复杂度 \(\mathcal O(n\log_{42}qV\times \log n)\)

CF571D Campus

离线,对每次集合的合并分别建出 Kruskal 重构树。对于第 5 类询问 \(x\),在第二类集合中找到最后一次涉及到 \(x\) 的置 0 操作,离线后就变成一个前缀查询。

第 3 类操作等价于在 Kruskal 上子树加,用树状数组维护即可。

时间复杂度 \(\mathcal O(n\log n)\)

CF407E k-d-sequence

区间 \([l ,r]\) 满足条件当且仅当:

  • 所有数 \(\bmod d\) 同余。
  • 不存在重复元素。
  • \(\dfrac{\max - \min}{d} - (r - l) \leq k\)

先找到 \(\bmod d\) 同余的所有极长区间,用一些简单套路维护。

时间复杂度 \(\mathcal O(n\log n)\)

注意特判 \(d = 0\)

CF700D Huffman Coding on Segment

首先转化题意,可以发现这就是哈夫曼编码,设 \(occ_i\) 表示 \(i\) 出现次数,问题即求 \([l, r]\) 中所有 \(i\) 构成的最小的 Huffman 树带权深度,即 \(\sum\limits_{i}occ_i \times d_i\)

而求解 Huffman 树的算法就是合并果子,开一个小根堆,每次取出最小的两个元素,答案累加和,再加入他们的和。

先用莫队求出 \(occ_i\),由于不同的 \(occ_i\) 最多 \(\mathcal O(\sqrt n)\) 个,一个想法是开一个链表记录所有 \(>0\)\(occ_i\),设 \(oc_i\) 表示 \(occ_p = i\)\(p\) 的个数,从小到大合并。若存在奇数个则额外用一个变量记录,可以通过。

还有一种想法是再根分一波,设 \(B = \sqrt n\),对于 \(i \leq B\) 暴力套用第一个做法,若合并出 \(> B\) 的数则加入小根堆。对于 \(>B\) 的数直接加入小根堆。然后就真的按照合并果子模拟。

时间复杂度 \(\mathcal O(n \sqrt n)\)\(\mathcal O(n\sqrt {n\log n})\)

CF633H Fibonacci-ish II

暴力 \(\mathcal O(n^2)\) 都能过,不懂为什么开 \(n = 30000\)

显然莫队 + 树状数组, \(\mathcal O(n\sqrt n \log n)\)

CF453E Little Pony and Lord Tirek

注意到每次查询区间后马上推平,可以想到 ODT。设 \((l, r, v)\) 表示区间 \([l, r]\) 推平的时刻都是 \(v\),则每次考虑一段区间的贡献时,若是第一次推平,贡献为 \(\sum \min(m_i, s_i+tr_i)\),左边取最值时 $m \leq s+tr \Rightarrow t \geq \left\lceil\dfrac{m - s}r\right\rceil $, 贡献为 \(m_i\),否则贡献为 \(s_i+tr_i\) 。可以用主席树维护。但稍加思索发现,反正都是第一次操作,总的个数只有 \(n\) 个,暴力就行了。

否则贡献为 \(\min(m_i, (t - v)r_i)\),同理分析可得 \(t \geq \left\lceil\dfrac{m}r\right\rceil +v\) 时取到 \(m_i\),还是可以用主席树维护之。

时间复杂度 \(\mathcal O(n\log n)\)

模拟赛有个加强版:从区间询问变成树上距离 \(x\) 不超过 \(d\) 的子树内的答案,做法就是先按照 dfs 序排序后分块,每块内部再按照深度排序,这样每次查询就是区间的形式,对应到一个块内就是一段前缀 split 查询,散块暴力。时间复杂度 \(\mathcal O(n\sqrt n \log n)\),按照 @Salieri 的说法,可以先把 ODT 换成栈减少 set 的 $\log $,而每次查询是一个静态的二维偏序问题,总共有 \(n \sqrt n\) 个,差分后对每个块离线扫一遍,再加上亿点点的细节就可以做到 \(\mathcal O(n \sqrt n)\),而且应该常数很小。

CF536E Tavas on the Path

讲询问离线按照 \(l\) 升序排序,相当于每次把一些边从 1 变成 0。然后直接树剖,每个节点维护最靠左的连续段颜色及长度,最靠右区间颜色及长度,非边界的答案,每次讨论一下 push_up 就好了。

时间复杂度 \(\mathcal O(n\log^2 n)\)

CF855F Nagini

用线段树加入同时存在 \(>0\)\(<0\) 的位置,可以对正负数开两个线段树在线段树上二分得到。

然后用 segment tree beats 维护正负数区间取 \(\min\) 即可,注意只统计加入的位置。

每个位置最多被加入一次,时间复杂度 \(\mathcal O(n \log n)\)

CF1332G No Monotone Triples

根据 Diliworth 定理,当 \(n \geq 5\) 时必然存在单调三元组(当然可以通过枚举直接得到)。

分类讨论 \(n = 2, 3, 4\) 的情况。

\(n = 2\) 当且仅当 \([l, r]\) 为单调区间。

\(n = 3\) ,对每个 \(i\) 找到左边最大的 \(>a_i\)\(j\),右边最小的 \(>a_i\)\(k\);或者左边最大的 \(<a_i\)\(j\),右边最小的 \(<a_i\)\(k\)。显然 \((j, i,k)\) 是一组合法方案。

\(n = 4\) 当且仅当两个端点都不是最值,考虑枚举最右端的 \(x\),考虑找到最优秀的最大和最小值,容易发现 \(p_2 ,p_3\) 只能分别在前缀最大 / 最小单调栈中取到。

\(p_4\) 一定不在任何一个单调栈内,否则它会成为最值。

在两个单调栈上二分出第一个 \(>a_x\)\(<a_x\) 的位置,设为 \(y, z\),找到 \(\max(y, z)\) 之后的第一个 “空隙”,即不在任何一个单调栈中的元素,这一定是最靠左的 \(p_4\)\(p_3\) 就取 \(p_4\) 之前的第一个元素,\(p_2\) 同理。

找出这些 \((p_1, p_4)\) 后离线二维数点即可。找 \(p_2, p_3, p_4\) 的过程可以在单调栈上二分和线段树上二分。时间复杂度 \(\mathcal O(n\log n)\)

CF1476G Minimum Difference

和出现次数有关的问题,根据经典结论不同出现的个数是 \(\mathcal O(\sqrt n)\) 的性质,且本题恰好也只要求出现次数的次数,于是只要我们把区间的这些出现次数的出现次数找出来,双指针扫一遍就是答案。

显然可以用带修莫队维护,由于要支持 \(\mathcal O(1)\) 加入删除,可以用链表维护,最后在查询时提取这些出现次数排序即可。时间复杂度 \(\mathcal O(nm^{2/3})+m\sqrt n \log n)\)。 可以用基数排序优化,但是没有必要。

CF960H Santa's Gift

拆询问,即 \(S_i^2\times b_x - 2S_ib_x - C^2\),显然只需要对每个颜色维护 \(S_i^2, S_i\) 即可,树剖解决。

CF792F Mages and Monsters

观察到问题类似线性规划,设 \(x_i\) 表示第 \(i\) 个法术的使用时间,则:

\[\min \sum\limits_{i = 1}^nx_i \\ \]

满足约束:

\[\sum\limits_{i = 1}^{n}x_iv_i \geq h \\ \sum\limits_{i = 1}^{n}x_ic_i \leq m \Leftrightarrow \sum\limits_{i = 1}^{n}x_i(-c_i) \geq -m \]

考虑其对偶变量,设为 \(A, B\),则满足:

\[\max Ah - Bm \]

满足约束:

\[v_iA - c_iB \le 1 \ \ \forall i \in [1, n] \]

由于线性规划对于每个变量都是凸的,于是可以三分 \(B\),变成 \(A \leq \dfrac{1}{v_i}+\dfrac{c_i}{v_i}B\),可以用李超线段树求出 \(A\) 的范围。

显然根据定义,\(A,B\) 非负,因此 \(A\) 直接取到上界一定是最优的,然后再判断 \(Ah-Bm\)\(t\) 的关系即可。

CF720D Slaom

由于对于所有障碍经过方式(从上 / 左经过)相同的方案是相同的,考虑找到一个关键路径统计答案。不妨直接找最低路径统计(在相同方案中所有点都是最低的)。

\(f_{i, j}\) 表示目前在 \((i, j)\) 的方案,考虑如何转移。

没有障碍时显然 \(f_{i, j} = f_{i - 1, j}\)

否则假设障碍在 \([l, r]\),之前在 \([l, r]\) 的贡献都应该统计到 \(r+1\) 上,但是不止于此,设 \(<l\) 的右端点为 \(p\),则 \([p+1, r]\) 这一段区间的 \(f_{i - 1, j}\) 都可以贡献到 \(f_{i, r+1}\)。用线段树维护,支持区间查询,赋值即可。

至于如何找到 \(p\),将矩形类似扫描线挂在对应端点,用一个 set 实时维护当前的线段即可。

注意最后的答案要找到最后一个区间 \([l, r]\),询问 \([r+1, m]\) 的答案。

CF1344E Train Tracks

首先并不是要等到火车完全走完边后再切换,而是花费 1 时间切换后就已经不用管然后可以继续切换。先考虑如何写暴力:对于同一个点而言,若在 \(t_1\) 时刻存在经过该点的火车,\(t_2\) 时刻又存在,则在 \([t_1+1, t_2]\) 时刻必然要切换一次 \(x\)。把所有形如:在 \([l, r]\) 中至少有一次切换某点的限制找出来后,问题变成:

有若干个区间 \([l, r]\),每秒可以选择一个未标记的且 \(l \leq t \leq r\) 的区间进行标记,问是否能把所有区间标记

显然可以贪心,维护一个小根堆,每次把 \(r\) 最小的弹出,若 \(r<t\) 则报告无解。

考虑如何找到这些区间,注意到“切换儿子并更新当且点最后一次标记时间戳“ 类似 access。于是用 lct 维护,每次 access x 时,如果有儿子,就表示 \(x\) 要切换一次,显然总切换次数是 \(\mathcal O(n\log n)\) 的,且设 \(x\) 深度为 \(d\),上一次覆盖标记为 \(lst\),就要在 \([lst+d_x+1, cur+d_x]\) 切换一次,把这 \(\mathcal O(n\log n)\) 条路径找出来后贪心即可。

时间复杂度 \(\mathcal O(n\log^2n )\)

注意到能合并就合并是最优的,因为无论是偶 + 偶,奇 + 偶,奇 + 奇都不会增加奇数个数,于是对于单次询问,可以考虑类似 Kruskal 的过程得到答案。

注意到答案单调不升,可以考虑整体二分。

注意到每次加边的变化只有当两边子树都是奇数才有用,可以直接上 LCT。

注意到若我们从后往前处理,将边集排序后维护一个指针表示当前答案,对于已经加入答案的边而言我们需要在某一时间段维护其合法范围,就可以直接线段树分治维护。

时间复杂度 \(\mathcal O(n\log n \log m)\)

CF1446D2 Frequency Problem (Hard Version)

容易用调整法证明两个答案其中一个为全局众数,设为 \(maj\)

暴力就是再枚举另一个元素 \(x\),将 \(x\) 看做 1,\(maj\) 看做 -1,答案就是最长的和为 0 的段。

还有一个暴力:直接枚举出现次数 \(cnt\),双指针枚举 \(maj\) 的第一次出现位置,对 \([l, r]\) 的这些数开桶记录出现次数为 \(i\) 的有多少个,当 最大值恰为 \(cnt\) 且个数 \(\geq 2\) 则存在。

结合上面两个算法,考虑根号分治,对于 \(occ_i > B\) 的数直接 \(\mathcal O(n)\) 算,否则直接枚举 \(cnt\),也可以 \(\mathcal O(n)\) 算。

时间复杂度 \(\mathcal O(n\sqrt n)\)

CF997E Good Subsegments

显然就是 CF526F 的加强版。考虑记录 \(\max - \min - (r - l)\) 最小值,最小值个数,用历史线段树维护之。

每次将右端点扩展 1 时,需要对所有 \(t_i = 0\) 的位置 \(s_i \leftarrow s_i+1\),直接打一个只对最小值生效的 tag 即可。

CF319E Ping-Pong

注意到若两个区间相交但不包含,则二者双向可达。若一个大的包含一个小的,则只能从大区间到小区间。

不妨直接将第一类区间用并查集合并看做一个点,容易发现每次询问最多走一次大区间到小区间。对于一次从 \(x\) 走向 \(y\) 的询问,若 \(x ,y\) 已经在同一连通块或 \(x, y\) 为包含关系则可以到达。

接下来考虑如何合并只相交的线段,注意到插入的线段长度单调不降,考虑将一个线段 \([l, r]\) 加入 \([l+1, r - 1]\) 的集合中,每次查询 \(l\)\(r\) 中的元素合并,同时剩下的线段没有用,并清空集合变成合并后的大集合。

时间复杂度 \(\mathcal O(n\log n\alpha(n))\)

CF1163F Indecisive Taxi Fee

CF436F Banners

前面 \(\sum\limits_{b_i \geq c}w \times c\) 是平凡的,对于后面的 \(\sum\limits_{b_i < c, a_i \geq p}p\) 不妨扫描线,每次加入若干个 \(a_i\),对于 \(p\) 开线段树维护答案。则每次等价于将所有 \(p \leq a_i\) 的位置加上 \(y = x\) 的函数。问题变为:

维护一个初始全 0 的序列,支持对一个前缀加上一个一次函数,询问全局 $\max $。

很可惜李超树并不能维护一次函数相加后的 \(\min\),考虑分块,每块内部记录 \(tg\) 和初始值 \(a_i\),则 \(i\) 的贡献是 \(a_i + tg \times i\),根据经典结论将每个 \(i\) 看做 \((i, a_i)\),则只有上凸包中的点有用,由于每次斜率单减直接开指针扫,散块直接暴力重构。

时间复杂度 \(\mathcal O(n \sqrt n)\)

CF793F Julia the snail

先离线扫,将 \([l_i, r_i]\) 挂在 \(r_i\),用线段树对每个 \(l\) 维护初始在 \(l\),在经过所有不超过 \(r\) 的绳子能到达的最大高度,对于一个 \([l_i, r_i]\) 而言,等价于将所有 \(i \in [1, l_i], a_i \geq l_i, a_i \leftarrow \max(a_i, r_i)\)

由于 \(r\) 是单调枚举的,也可以看做 \(a_i \leftarrow r\),即区间 \(\geq\) 某值的赋值。

使用吉司机线段树(然鹅并不会势能分析) 维护。

CF1178G The Awesomest Vertex

显然 \(|\sum b|\) 是常数,问题变成每个点是 \(a|x|(a\geq 0)\),每次区间 \(x\) 加一个正数 \(v\),求区间最大。

显然不能 polylog,考虑分块,首先 \(|x| = \max(x, -x)\),维护 \(-x\)\(x\) 两次取 \(\max\) 即可。

此时问题变成 CF436E。

CF773E Blog Post Rating

手玩发现 F 一定是先单减后不降。

F 第一次不降的位置 \(i\) 满足 \(a_i \geq -i\),对于剩下的位置有 \(f_i = \min(f_{i - 1}+1, a_i)\)。答案是 \(f_n\)

用值域线段树维护所有已经加入的位置,每次线段树二分先找到第一个分界点 \(p\),在 \(p - 1\) 的答案是 \(-(p- 1)\)

\(f_n\) 的柿子拆开,\(ans = \min(a_n, a_{n - 1}+1, \dots, a_{p}+n - p, n - p+1 -(p - 1))\)

再维护 \(a_p - p\) 的区间 \(\min\),每次查询就变成了后缀 \(\min\)

时间复杂度 \(\mathcal O(n\log n)\)

CF765F Souvenirs

不妨设 \(j < i, a_i< a_j\),考虑哪些二元组 \((i, j)\) 可能成为最优决策:必然是单调递增的单调栈中所有 \(\geq a_i\)\(j\)。当然,不能直接枚举所有 \(j\),但发现若 \(k<j, a_k < a_j, a_j - a_k < a_k - a_i\),则该部分已经在 \((k, j)\) 算过了,所以合法的 \(k\) 必然满足 \(a_k - a_i<a_j - a_k \rightarrow a_k <\dfrac{a_i+a_j}2\)

出现了值域减半,因此总共的 \(k\) 最多有 \(n\log n\) 个,用一个线段树找一下即可,然后用树状数组维护一下二维数点。

CF679E Bear and Bad Powers of 42

先粗略估一下 \(a_i \leq qV\),而 \(42^{10} >10^{14}\),因此合法的 power 极少,直观上应该是一个势能分析。

考虑记录每个 \(a_i\) 与其最近的 \(42\) 幂次的距离,操作 3 相当于若区间最小值 = 0 则不断做一个区间减法,减完后若 \(<0\) 则暴力更新新的距离。则花费在每个数上的复杂度为 \(\mathcal O(\log_{42}qV \times \log n)\)

现在加上区间赋值,感觉用 ODT 复杂度摊着摊着就对了!但不能直接用 ODT 来表示连续段,因为这会使线段树区间发生变化。

考虑直接在线段树上区间赋值,这等价于将每个连续段再拆成了 \(\log n\) 个段,此时再考察操作 3:

  • 若线段树区间存在赋值标记,更新赋值标记重新计算距离。
  • 否则不断递归直到区间最小值 \(\geq x\) 或者到达叶子节点。

时间复杂度 \(\mathcal O(n\log_{42}qV\times \log n)\)

CF571D Campus

离线,对每次集合的合并分别建出 Kruskal 重构树。对于第 5 类询问 \(x\),在第二类集合中找到最后一次涉及到 \(x\) 的置 0 操作,离线后就变成一个前缀查询。

第 3 类操作等价于在 Kruskal 上子树加,用树状数组维护即可。

时间复杂度 \(\mathcal O(n\log n)\)

CF407E k-d-sequence

区间 \([l ,r]\) 满足条件当且仅当:

  • 所有数 \(\bmod d\) 同余。
  • 不存在重复元素。
  • \(\dfrac{\max - \min}{d} - (r - l) \leq k\)

先找到 \(\bmod d\) 同余的所有极长区间,用一些简单套路维护。

时间复杂度 \(\mathcal O(n\log n)\)

注意特判 \(d = 0\)

CF700D Huffman Coding on Segment

首先转化题意,可以发现这就是哈夫曼编码,设 \(occ_i\) 表示 \(i\) 出现次数,问题即求 \([l, r]\) 中所有 \(i\) 构成的最小的 Huffman 树带权深度,即 \(\sum\limits_{i}occ_i \times d_i\)

而求解 Huffman 树的算法就是合并果子,开一个小根堆,每次取出最小的两个元素,答案累加和,再加入他们的和。

先用莫队求出 \(occ_i\),由于不同的 \(occ_i\) 最多 \(\mathcal O(\sqrt n)\) 个,一个想法是开一个链表记录所有 \(>0\)\(occ_i\),设 \(oc_i\) 表示 \(occ_p = i\)\(p\) 的个数,从小到大合并。若存在奇数个则额外用一个变量记录,可以通过。

还有一种想法是再根分一波,设 \(B = \sqrt n\),对于 \(i \leq B\) 暴力套用第一个做法,若合并出 \(> B\) 的数则加入小根堆。对于 \(>B\) 的数直接加入小根堆。然后就真的按照合并果子模拟。

时间复杂度 \(\mathcal O(n \sqrt n)\)\(\mathcal O(n\sqrt {n\log n})\)

CF633H Fibonacci-ish II

暴力 \(\mathcal O(n^2)\) 都能过,不懂为什么开 \(n = 30000\)

显然莫队 + 树状数组, \(\mathcal O(n\sqrt n \log n)\)

CF453E Little Pony and Lord Tirek

注意到每次查询区间后马上推平,可以想到 ODT。设 \((l, r, v)\) 表示区间 \([l, r]\) 推平的时刻都是 \(v\),则每次考虑一段区间的贡献时,若是第一次推平,贡献为 \(\sum \min(m_i, s_i+tr_i)\),左边取最值时 $m \leq s+tr \Rightarrow t \geq \left\lceil\dfrac{m - s}r\right\rceil $, 贡献为 \(m_i\),否则贡献为 \(s_i+tr_i\) 。可以用主席树维护。但稍加思索发现,反正都是第一次操作,总的个数只有 \(n\) 个,暴力就行了。

否则贡献为 \(\min(m_i, (t - v)r_i)\),同理分析可得 \(t \geq \left\lceil\dfrac{m}r\right\rceil +v\) 时取到 \(m_i\),还是可以用主席树维护之。

时间复杂度 \(\mathcal O(n\log n)\)

模拟赛有个加强版:从区间询问变成树上距离 \(x\) 不超过 \(d\) 的子树内的答案,做法就是先按照 dfs 序排序后分块,每块内部再按照深度排序,这样每次查询就是区间的形式,对应到一个块内就是一段前缀 split 查询,散块暴力。时间复杂度 \(\mathcal O(n\sqrt n \log n)\),按照 @Salieri 的说法,可以先把 ODT 换成栈减少 set 的 $\log $,而每次查询是一个静态的二维偏序问题,总共有 \(n \sqrt n\) 个,差分后对每个块离线扫一遍,再加上亿点点的细节就可以做到 \(\mathcal O(n \sqrt n)\),而且应该常数很小。

CF536E Tavas on the Path

讲询问离线按照 \(l\) 升序排序,相当于每次把一些边从 1 变成 0。然后直接树剖,每个节点维护最靠左的连续段颜色及长度,最靠右区间颜色及长度,非边界的答案,每次讨论一下 push_up 就好了。

时间复杂度 \(\mathcal O(n\log^2 n)\)

CF855F Nagini

用线段树加入同时存在 \(>0\)\(<0\) 的位置,可以对正负数开两个线段树在线段树上二分得到。

然后用 segment tree beats 维护正负数区间取 \(\min\) 即可,注意只统计加入的位置。

每个位置最多被加入一次,时间复杂度 \(\mathcal O(n \log n)\)

CF1332G No Monotone Triples

根据 Diliworth 定理,当 \(n \geq 5\) 时必然存在单调三元组(当然可以通过枚举直接得到)。

分类讨论 \(n = 2, 3, 4\) 的情况。

\(n = 2\) 当且仅当 \([l, r]\) 为单调区间。

\(n = 3\) ,对每个 \(i\) 找到左边最大的 \(>a_i\)\(j\),右边最小的 \(>a_i\)\(k\);或者左边最大的 \(<a_i\)\(j\),右边最小的 \(<a_i\)\(k\)。显然 \((j, i,k)\) 是一组合法方案。

\(n = 4\) 当且仅当两个端点都不是最值,考虑枚举最右端的 \(x\),考虑找到最优秀的最大和最小值,容易发现 \(p_2 ,p_3\) 只能分别在前缀最大 / 最小单调栈中取到。

\(p_4\) 一定不在任何一个单调栈内,否则它会成为最值。

在两个单调栈上二分出第一个 \(>a_x\)\(<a_x\) 的位置,设为 \(y, z\),找到 \(\max(y, z)\) 之后的第一个 “空隙”,即不在任何一个单调栈中的元素,这一定是最靠左的 \(p_4\)\(p_3\) 就取 \(p_4\) 之前的第一个元素,\(p_2\) 同理。

找出这些 \((p_1, p_4)\) 后离线二维数点即可。找 \(p_2, p_3, p_4\) 的过程可以在单调栈上二分和线段树上二分。时间复杂度 \(\mathcal O(n\log n)\)

posted @ 2023-03-26 21:40  henrici3106  阅读(66)  评论(0编辑  收藏  举报