数据结构选做
CF878D
答案显然只有 \(k\) 种,由于整个题只考虑数值的大小关系,考虑枚举答案后用经典技巧转化变成 \(a_i\in \{0,1\}\) 的问题。然后发现本质不同的列只有 \(O(2^k)\) 种,换句话说我们可以维护 \(f_{i,S}\) 表示初始列上的数字状态为 \(S\) 时,这一列上的第 \(i\) 个数字是多少。可以用 bitset 优化至 \(O(\frac{q2^k}w)\),查询的时候枚举答案查表即可。
CF840E
观察数据范围猜测要分块,那么我们把 \(v\to u\) 路径上每 \(B\) 个分一块,散块暴力处理,整块单独做。考虑此时一块内的代价,假设这一块块底节点为 \(x\) 且是 \(v\) 向上的第 \(i\) 个块,那么其贡献 \(f_{x,i}=\max\limits_{j=0}^{B-1} \{a_{fa(x,j)}\oplus (B\times i+j) \}\)。
注意到如果我们取 \(B\) 为 \(2^k\),那么后面的 \(B\times i+j\) 实际上可以分成高低两部分。取 \(B=2^8\),此时单个点贡献被拆分为高 \(8\) 位和低 \(8\) 位。优先让高位更大,就是让 \(a_{fa(x,j)}\) 的高 \(8\) 位异或上 \(i\) 最大,01-Trie 维护即可。然后低 \(8\) 位用一个数组存一下高 \(8\) 位确定后的最大值即可。这样我们就求出了 \(f_{x,i}\),复杂度 \(O(n\sqrt n \log n+q\sqrt n)\)。
CF1508F
观察到被包含的区间一定不优,去掉以后区间只有相交。单独考虑一个区间内的连边直接按数字大小顺次相连即可得到限制,并且如果同时存在 \(u\to v_1\to v_2\to \cdots\to v_k\to w\) 和 \(u\to w\) 限制那么后者可以删掉。
接下来拆贡献,考虑这 \(O(n^2)\) 条边会在哪些时刻出现,显然出现的时刻构成一个区间。区间起点容易求,二维数点一下即可。区间终点的话考虑如果当前边为 \((u,v)\),若存在 \(w\) 满足 \(a_u<a_w<a_v\),那么如果一个区间同时包含 \(u,v,w\) 时这条边就会被删掉。那么只需在左边和右边分别找到第一个满足条件的 \(w\) 然后做一个二维数点即可。暴力做是 \(O(n^2)\) 的。
接下来需要一些技巧,注意到如果对这 \(q\) 个区间做莫队,那么实际上有用的边数只有 \(O(n\sqrt q)\) 条,具体实现时由于链表容易删除而不容易插入,可以通过不插入莫队做到 \(O(n\sqrt q)\) 求边集。接下来求 \(w\) 的部分实际上也是一个二维数点,需要实现 \(O(n)\) 次单点修改,\(O(n\sqrt q)\) 次区间查最值,分块套 ST 表平衡做到复杂度 \(O(n\sqrt q+n\sqrt n)\)。最后的二维数点同样用分块平衡,可以做到总复杂度 \(O(n\sqrt q+q\sqrt n)\),若 \(n,q\) 同阶则复杂度可以做到单根号。
CF1990F
套路题。我们知道合法的区间满足 \(sum(l,r)>2\times max(l,r)\),感受一下应该能猜到这个题要用到倍增复杂度的 trick。注意到对于区间 \([l,r]\),若 \(a_{l-1}< sum(l,r)\),则加入 \(l-1\) 一定不劣,反之则一定不行。所以最优的区间应该满足 \(\max(a_{l-1},a_{r+1})<sum(l,r)\)。
上线段树维护,根据经典结论,我们需要维护当前区间内的最优前缀和最优后缀。显然对于最优前缀满足 \(sum(l,p)\le a_{p+1}\),因此最优前缀每扩大一次和至少翻倍,总共只有 \(O(\log V)\) 个。单次暴力合并复杂度 \(O(\log^2 V)\),双指针即可做到 \(O(\log V)\),总复杂度 \(O(n\log V+q\log n\log V)\)。
CF2115F
显然直接维护每种集合根本不可能,考虑在操作上处理。可以注意到的是对于 \(m\) 个修改,序列只会被分成 \(O(m)\) 段,每一段内集合相同。因此我们只需维护 \(O(m)\) 个信息,每个信息形如区间 \([l,r]\) 被挪动到了 \([l',r']\) 且是否被翻转。线段树维护这些信息,那么每一次合并的复杂度是 \(O(len \log len)\),根据主定理总复杂度 \(O(q\log^2 q)\)。虽然题目强制在线但是我们可以只保留所有有效节点,这样复杂度不变。
接下来考虑查询,对于每个信息,我们再维护一个标记,表示这个区间在这些操作中是否被插入值。同时由于我们维护了完整信息,所以如果知道操作结束后点的位置可以查询出操作前点的位置。那么这二者就可以让我们通过线段树上二分来找出第一个影响到查询点的修改,那么显然这就是集合中的最小值。最难考察的是删除操作,实际上删除操作影响到的就是刚才提到的标记,显然这个标记是由左右儿子的某两个区间的标记拼接而成。而换句话说,我们可以对于每个区间,维护它往其父亲的哪些区间贡献了自己的标记,当一个点的标记被归零的时候去修改这些区间的标记即可,用一个队列就可以实现这个操作。那么可以发现每个区间的标记最多被修改 \(2\) 次就会归零,所以删除的总复杂度和总区间数相等。综上总复杂度 \(O(q\log^2 q)\)。
P4898
首先由于网格是二维的,我们肯定不能维护下标相关的信息,所以考虑维护值的信息,那么现在我们需要快速判断一个前缀的值 \([1,k]\) 在网格中是否恰好构成了一个矩形。
这其实是一个经典的技巧,考虑网格中所有 \(2\times 2\) 的小正方形(边界上的也算),并记 \(c(x)\) 表示满足小正方形中恰有 \(x\) 个黑色格子的小正方形数量,注意到如果构成一个完整的矩形那么一定满足 \(c(1)=4\)。注意到一个图形无论如何一定满足 \(c(1)\ge 4\),所以这应该可以排除掉绝大多数不联通或者明显不满足要求的形状。此时只有一种情况会爆掉就是一个矩形中扣掉一些矩形。那么我们再考虑统计内部的这些矩形,显然如果有这种情况,那么 \(c(3)>0\),而完整矩形满足 \(c(3)=0\) 取到最小值。
因此完整矩形同时取到 \(c(1),c(3)\) 最小值,并且这个条件是充要的。因此利用线段树维护出每个前缀的 \(c(1)+c(3)\) 的值,并维护最小值个数即可。修改只会修改 \(O(1)\) 个小正方形的信息,也可以直接做。复杂度 \(O((nm+q)\log nm)\)。
P6774 / P6579
首先经典的分析方式是把点放到坐标系上考虑,然后直接求解矩形 \([l_1,r_1]\times [l_2,r_2]\) 中顺序对的个数显然是困难的,于是可以考虑容斥。平凡的情况都是二维数点,本质上只有一种非平凡情况:统计 \(A\in [l_1,r_1]\times [1,l_2),B\in [l_1,r_1]\times [l_2,r_2]\) 的顺序对 \((A,B)\) 数量。
此时可以考虑分块,先考虑块内贡献如何计算。注意到如果下标 \(i,j\) 是一个顺序对,那么他会对满足 \(l_2\in [p_i+1,p_j],r_2\in[p_j,\infty)\) 的值域区间做出贡献,由于一个块内只有 \(O(B)\) 个点,所以可以离散化后暴力二维前缀和做到 \(O(nB)\) 复杂度。然后考虑整块之间以及散块的贡献,这个是容易的,只需要算前面的块中有多少个 \(A\) 点以及后面的块中有多少个 \(B\) 点,用一个变量 \(cnt\) 累加 \(A\) 点的个数即可。
上述过程在每一块离散化后不需要带 $\log $ 即可完成,并且我们可以对于每一块离线做,这样的话空间复杂度可以做到线性。取 \(B=\sqrt n\) 可以做到时间复杂度 \(O(n\sqrt n)\)。
P8078 / P11367
首先直接上不插入莫队可以直接做到 \(O(n\sqrt m)\),但是这题有 \(\text{polylog}\) 做法。
考虑分治,我们在分治区间 \([l,r]\) 中处理所有跨过 \(mid\) 的询问 \([L,R]\)。由于左右两边对称,我们只考虑计算右半边的贡献。假设 \(a_{mid+1}\sim a_{R}\) 中的所有数字排序后依次是 \(a'_1,a'_2,\cdots ,a'_k\),考虑相邻两个数字对 \((a'_x,a'_{x+1})\) 的贡献:
- 如果存在 \(i\in [L,mid]\),使得 \(a'_x<a_i<a'_{x+1}\),那么我们的贡献可以看做 \(p_x+p_{x+1}-2\cdot mid\)。
- 否则贡献一定就是 \(|p_x-p_{x+1}|\)。
我们可以对每个数字对维护 \(mx_x\) 表示最大的满足 \(a'_x<a_i<a'_{x+1}\) 的 \(i\),那么统计贡献的时候相当于求区间和。然后考虑如何维护 \(mx_x\),如果从 \(mid+1\) 往 \(r\) 扫插入数字比较困难,不妨考虑倒着删除数字,这样的话只需要用链表维护,合并的时候直接将两个 \(mx_x\) 取最大值即可。这样的话我们的总修改次数为 \(O(n\log n)\),查询次数为 \(O(m)\),复杂度 \(O(n\log^2n+m\log n)\),使用多叉线段树平衡可以做到 \(O\left(\dfrac{(n+m) \log^2 n}{\log \log n}\right)\) 的复杂度。
P8257
扫描线后转化为如下问题:区间 \(+1\),求区间历史异或和,令 \(b_i\) 为历史异或和数组。考虑分块,先来考虑整块相关的部分,修改的时候维护 \(tag_o\) 表示增量,查询的时候维护一个 \(sum_o\) 表示块内的历史异或和,问题在于每一次更新 \(sum_o\) 的时候,我们需要求出 \(\oplus_{i=l_o}^{r_o} (a_i+x)\),设这个值为 \(f_{o,x}\),如果我们能动态维护每个块的 \(f\),那么更新历史和的时候直接令 \(sum_o\leftarrow sum_o\oplus f_{x,o}\) 即可。
那么实际上我们只需要关注一个块被重构时 \(f\) 如何维护即可,注意到 \(x\) 的最大值可以达到 \(O(n)\),而散块的重构次数是 \(O(n)\),所以直接维护无论如何是不能维护出来的。于是进一步的,我们考虑定期重构,对于一个整块,如果 \(tag_o>B\) 我们也让它重构一次 \(f\),这样可以保证 \(x\) 为 \(O(B)\),并且重构次数仍为 \(O(\frac{n^2}{B^2})=O(n)\)。
现在我们考虑如何单次 \(O(B)\) 重构一个块,根据经典套路可知 \(a+b\) 等于 \(a\oplus b\) 再异或上 \(a+b\) 产生的所有进位,那么我们只需统计进位数,换句话说对所有 \(k\) 统计 \(a_i\bmod 2^k+x\bmod 2^k\ge 2^k\) 的 \(a_i\) 的个数。暴力枚举所有 \(k\) 只能做到 \(O(B \log n)\),考虑根据 \(B\) 再做一次分类。如果 \(2^k\le B\),那么取模后的值的总量只有 \(O(B)\) 个,暴力统计即可;而如果 \(2^k>B\),此时 \(x\bmod 2^k\) 不随 \(k\) 改变,那么每个 \(a_i\) 只会给固定的一段 \(x\) 做相同的贡献,复杂度也是 \(O(B)\) 的。
然后你会发现 \(f\) 就解决完了,但是在重构块的时候,我们还需要考虑的是如何重构 \(b\)。考虑记录上一次重构到本次重构之间,我们对哪些 \(tag_o\) 取过历史和,换句话说我们维护一个集合 \(S\),那么实际上当前的 \(b_i\) 应该异或上 \(\oplus_{x\in S} (a_i+x)\),这个式子和 \(f\) 的式子可以说如出一辙,但是我们似乎无法保证 \(a_i\le O(B)\),仔细从实际意义考虑一下,\(a\) 是颜色数,那么同一个块内 \(a\) 的极差一定不超过 \(B\),那么我们给 \(a_i\) 都减去最小值,给 \(x\) 加上最大值,即可保证新的 \(a_i\) 是 \(O(B)\) 的。
于是对于一个整块,重构 \(b\) 和 \(f\) 的部分就讨论完了,整块重构解决后,剩下的部分就简单了。取 \(B=O(\sqrt n)\),复杂度是严格单根号 \(O(n\sqrt n)\) 的。
P10574
首先需要发现一个关键性质:修改一个 \(x\) 之后,只会对其根链上的点的 \(f\) 造成影响,并且影响实际上只有 \(O(\sqrt n)\) 段。原因很简单,考虑根链上有多少个点的子树中有深度和 \(x\) 相同且不为 \(x\) 的点,发现每次往父亲跳一步后额外需要的点数都会加一,而总点数是 \(O(n)\) 的。
那么现在的问题是找出这 \(O(\sqrt n)\) 个分界点,然后对每一段 chkmax。重链剖分,分类讨论一下:
- 对于一条重链顶的父亲节点,这样的点只有 \(O(\log n)\) 个,直接钦定他们是分界点不会让复杂度变化。对于求这些点的答案变化,对每一层维护一个树状数组然后直接查询即可。
- 剩下的是每条重链中间的点 \(u\),先维护出 \(u\) 的轻子树中距离 \(u\) 为 \(i\) 的所有点的点权和 \(f_{u,i}\)。查询的时候暴力做似乎还是不行,但是不要忘了本题对点数总和是有限制的,那么直接考虑阈值分治。对于和 \(x\) 距离 \(\le \sqrt n\) 的点暴力做,对于和 \(x\) 距离 \(>\sqrt n\) 的点,它们的轻子树中必须有距离它 \(>\sqrt n\) 的点,这样的点显然最多只有 \(O(\sqrt n)\) 个,提前预处理出所有这样的点然后暴力枚举即可。
求出分界点后用 segbeats 更新,复杂度理论上是 \(O(n\log n (\sqrt n+\log n))\) 的,但我们可以进行进一步的分析。
设所有分界点距离 \(x\) 的距离从小到大排序为 \(a_1,a_2,\cdots, a_k\),根据点数总和限制我们知道 \(\sum a_i\le n\),而根据经典结论我们知道在满足这个限制的情况下,\(\sum \log(a_i-a_{i-1})\) 的数量级最大是 \(O(\sqrt n)\) 的,也就是说如果我们将所有的段同时放到线段树上去操作的话,复杂度就可以变成 \(O(n\sqrt n+n\log^2 n)\) 了。
CF2255E
感觉很牛。由于本题强制在线所以我们无法换维扫描线,只能直接做。首先维护最大子段和可以直接采用经典的 \((sum,lmx,rmx,mx)\) 四元组合并,然后我们需要考虑的是如何打标记。
考虑标记的实质就是在每个节点维护了一个操作序列,对于一个叶子结点,从它开始向上不断跳,经过的每个节点的操作序列顺次连接起来就是这个点经过的所有操作。但是显然我们不能维护整个序列,因此核心难点是我们需要用尽可能少的信息维护出这个操作序列。
考虑当前节点上的所有操作,注意到在第一次覆盖操作完成后,后面所有的操作构成的四元组可以轻松求出,同时在这个序列后面动态加入一个操作后继续维护这个四元组也是简单的。因此我们只需要维护第一次覆盖操作后的部分构成的四元组,以及这个覆盖数最后会变成多少即可。
现在问题只在于第一次覆盖操作前的所有操作,这个时候我们并不知道被操作的数的大小,但是可以发现这道题的 2,3 操作都不基于数的绝对大小,我们只关心它和 \(0\) 的关系。于是可以仿照 E1 的做法,维护三个四元组表示 \(<0\),\(=0\),\(>0\) 的数在前面这个部分的贡献系数,以及这些数最后符号会变成什么就行了。
合并标记的时候分类讨论合并后的操作序列的第一次覆盖操作在哪一边即可,答案就是叶子结点的 \(mx\)。总复杂度 \(O(n\log n)\),当然时间和空间常数都比较大。
CF2152G
\(O(n\log^2 n)\) 的做法比较显然。考虑我们要求的就是黑点构成的虚树的所有叶子,树上问题我们一般拍到 DFS 序上,但是对于这道题把树拍到括号序上会更好做。只保留所有黑点的括号,叶子结点个数就是子串 () 的个数,那么只需要在线段树上维护黑点和白点的括号序列中 () 的个数,翻转的时候直接交换信息即可。复杂度 \(O(n\log n)\)。
P10882 / P12084
比较神。考虑值域分块,设 \(B_k=[2^k,2^{k+1})\),我们对于每个区间找到第一个 \(k\) 满足区间中有 \(\ge 3\) 个数,则这个区间中任选三个数一定都合法,选最小的三个即可。
然后需要考虑存在数字 \(<2^k\) 的情况,这里还需要进行进一步分类讨论。
-
有两个数字 \(<2^k\)。
注意到如果较小的两个数确定了,那么最大的数越小越好,也就是说将区间中 \(<2^k\) 的所有数排序后,最大的两个数一定相邻。根据这个结论我们也可以推出,\(\ge 2^k\) 的那个数一定是 \(B_k\) 中最小的那一个。‘
所以对于这个情况,我们将区间中 \(<2^k\) 的所有数和 \(\ge 2^k\) 的最小数弄一起排序,枚举最大的两个数,二分求出合法的最小值即可。由于 \(<2^k\) 每个块中最多两个数,所以数字个数为 \(O(\log V)\),总复杂度 \(O(n\log n \log V)\)。
可以通过单调性用双指针进一步优化至 \(O(n\log V)\),但不一定是复杂度瓶颈。
-
只有一个数字 \(<2^k\)。
根据上面的结论,我们选择的数字一定相邻。先判掉选了一个 \(\ge 2^{k+1}\) 的数的方案。接下来我们对每个块 \(B\),求出块中所有的数在整个序列上有哪些支配对。将块内数字按照从小到大排列并依次加入,假如当前插入 \(a_k\),对于 \(i<j<k\),已知 \((j,k)\) 是支配对的情况下,如果 \((i,k)\) 是支配对,则必有 \(a_i>\frac{a_j+a_k}2\),所以单次最多跳 \(O(\log V)\) 次,我们得到了 \(O(n\log V)\) 个支配对。
接下来我们统计答案相当于做一个三维数点(\(l\le i\le j\le r\) 且 \(|a_i-a_j|<a_e\)),但是修改和查询次数都是 \(O(n\log V)\) 次,复杂度比较爆炸。
优化复杂度的方向是,对于一个支配对 \((i,j)\),有一些 \(a_e\) 根本无法被它支配,这些点就无用了。因此更好的方式是对于每个 \(a_e\) 去看有多少个支配对可以和它组合成合法答案。考虑一个 \(a_e\),找出其前面第二次出现的位置 \(L\) 和后面第二个位置 \(R\),那么有用的支配对 \((i,j)\) 一定满足 \(L\le i\le j\le R\),在这个区间跑上面的算法。注意到所有的 \(a_e\) 对应的区间 \([L,R]\) 的长度总和为 \(O(n\log V)\),所以此时答案三元组 \((a_i,a_j,a_e)\) 数量为 \(O(n\log ^2 V)\)。
此时我们要做的是一个二维数点,修改数量为 \(O(n\log^2 V)\),查询数量为 \(O(q)\)。迭代分块做到 \(O(1)-O(n^{\epsilon})\),复杂度 \(O(n\log^2 V+qn^{\epsilon})\)。注意我们不可能存下所有的三元组,因此要边扫描线边求,空间可以做到 \(O(n\log V)\)。

浙公网安备 33010602011771号