数据结构做题记录-1

数据结构做题记录-1

CF1039D. You Are Given a Tree

考虑贪心,当子树内存在一条长度为 \(k\) 的链时不选肯定不优,因为我们一定会浪费父亲节点而链的总条数不变,于是可以维护子树内的两条最长链暴力判断即可,单次复杂度是 \(O(n)\) 的。

观察到答案单调不增,于是可以用整体二分求解单调队列的思路进行求解,看似这种做法是 \(O(n^2\log n)\) 甚至不如暴力的 \(O(n^2)\),但考虑到对于每种长度 \(k\)\(k·\text{ans}_k\le n\),于是所有位置的答案值只有 \(O(\sqrt{n})\) 个,那么我们二分的过程相当于在一棵极度平衡的二叉树上寻找每一个值的区间对应的点,根据经典结论,每一个区间对应的点是 \(O(\log n)\) 个,那么总体的复杂度就是 \(O(n\sqrt{n}\log n)\)

AGC001F. Wide Swap

考虑位置相差 \(\ge k\) 较为困难,但是要求值相邻,可以求出 \(P\) 的逆排列 \(Q\),那么在 \(Q\) 上操作就是要求交换相邻且值相差 \(\ge k\) 的位置。考虑到 \(P\) 的字典序最小意味着 \(Q\) 的反串的字典序最大(可以用数学归纳+反证法简单证明),那么如果交换的内容满足 \(Q_i>Q_{i+1}\),那么这是无意义的(可以用数学归纳+反证法简单证明)。于是每次只需要找到 \(Q_i\ge Q_{i+1}+k\) 的位置交换即可,用归并排序实现,当前位置能够放右区间当前数字当且仅当左边的后缀最小值 \(V\) 大于等于右边数字 \(+k\) 的值,于是可以简单做到 \(O(n\log n)\)

CF1578B. Building Forest Trails

先断环为链,那么两条弦相交当且仅当在链上对应的线段相交但不包含。考虑到连通块内部具体的连边不重要,不妨对于连通块内的所有相邻的点 \(l,r\) 连接 \((l,r)\) 这条线段,并称 \(h_i\)\(i\) 上覆盖的线段的个数。通过观察可以发现在同一个连通块内的点的 \(h\) 相同,同时还有 \(|h_i-h_{i+1}|=1\),考虑用这些性质维护。

我们维护一个并查集,对于新连接的线段 \((l,r)\),如果两个端点不在同一个连通块内,我们考虑以下几种情况:

  1. \(h_l\ne h_r\),不妨假设 \(h_l>h_r\),我们找到 \(l\) 右边第一个满足 \(h_p<h_l\) 的点,那么有 \(l\) 所在的连通块被 \(p\) 所在的连通块完全包含了,此时直接连接 \(l,p\) 所在连通块即可。
  2. \(h_l=h_r\),我们同理找到 \(p\),此时若 \(p<r\),那么依旧连接 \(l,p\) 所在连通块,否则 \(l,r\) 之间不再和其他的线段相交,没有产生新的连通块,直接将 \(l,r\) 所在连通块连接即可。

不难发现上面的内容可以用线段树维护区间最小值、区间加实现,复杂度是 \(O(q\log n)\) 的。

CF1129D. Isolation

首先不难写出 dp 转移:设 \(f_i\) 表示以第 \(i\) 个位置结尾的分段方案数,那么枚举上一段的结束位置 \(p\),计算 \([p+1,i]\) 中有多少个数恰好出现一次,记为 \(c(p,i)\),那么转移即为

\[f_i=\sum_{p=0}^{i-1}[c(p,i)\le k]f_p \]

考虑每次计算 \(i\),以 \(c(\sim,i)\) 可以在 \(c(\sim,i-1)\) 的基础上简单得出,只需要将一些区间 \(-1\),一些区间 \(+1\) 即可。可贡献中的 \(\le k\) 让我们没有办法用 \(O(\log)\) 数据结构维护,不妨考虑分块,对每个块维护 \(\le k\)\(c\) 的和即可。对于整块修改,我们通过修改位移标记快速实现,而单点修改,考虑单次的变动仅为 \(1\),因此在前缀和数组中只会修改一个位置,于是可以简单实现,复杂度是 \(O(n\sqrt{n})\) 的。

AGC015E. Mr.Aoki Incubator

先说结论:对所有点按照 \(V\) 从小到大排序,设 \(L_i\) 为最小的满足 \(X_p\ge X_i\)\(p\)\(R_i\) 为最大的满足 \(X_p\le X_i\)\(p\),如果初始时染了 \(i\),则最后 \([L_i,R_i]\) 中的所有点都会被染色。证明考虑分类讨论:

  1. \((V_{L_i}\le V_p< V_i\land X_p\ge X_i)\lor(V_i< V_p\le V_{R_i}\land X_p\le X_i)\),这样的 \(p\) 会直接和 \(i\) 相遇。
  2. \(V_{L_i}< V_p< V_i\land X_p<X_i\),这样的 \(p\) 会在 \(L_i\)\(i\) 相遇后和 \(L_i\) 相遇。
  3. \(V_i< V_p< V_{R_i}\land X_p> X_i\),这样的 \(p\) 会在 \(R_i\)\(i\) 相遇后和 \(R_i\) 相遇。
  4. \((V_p<V_{L_i}\land X_p\ge X_i)\lor(V_p>V_{R_i}\land X_p\le X_i)\),根据 \(L_i,R_i\) 的定义,这样的 \(p\) 不存在。
  5. \((V_p<V_{L_i}\land X_p<X_i)\lor (V_p>V_{R_i}\land X_p> X_i)\),这样 \(p\) 不会再任何点被染色后和其相遇。

可以结合坐标系理解,就是考虑对每个点标出 \((V_i,X_i)\),那么两点直接相遇当且仅当斜率为负,且斜率越大两点相遇越早。于是我们可以在 \(O(n\log n)\) 以内求出 \(L_i,R_i\)(存在 \(O(n)\) 的方法),并且考虑到不存在 \(L_i<L_j\le R_j<R_i\) 的情况,否则有 \(X_{R_i}>X_{L_i}\),于是我们对 \([L_i,R_i]\) 排序后进行 dp:设 \(f(i)\) 表示覆盖了 \([1,i]\) 的方案数,对于 \([L,R]\),可以从 \(f(L-1\sim R)\) 转移到 \(f(R)\),用前缀和优化可以简单做到 \(O(n)\)。综上,复杂度为 \(O(n\log n)\),复杂度瓶颈在排序。

AGC007E. Shik and Travel

最大值最小可以直接考虑二分,二分出 \(x\) 后一个简单的思路是设 \(f(u,a,b)\) 表示在 \(u\) 的子树内,\(u\) 到第一个叶子节点的距离为 \(a\)\(u\) 到最后一个叶子节点的距离为 \(b\) 是否可行,假设左儿子为 \(l\),右儿子为 \(r\),并假设先走左儿子,那么有转移

\[f(u,a,b)=\text{or}_{i,j}(f(l,a,i)\land f(r,j,b)\land i+j+v_l+v_r\le x) \]

考虑枚举左右儿子的合法状态求解,同样对先走右儿子转移即可,然而这样子做复杂度太高了。考虑到如果存在合法状态满足 \(a_i\le a_j,b_i\le b_j\),那么可以将 \((a_j,b_j)\) 这个状态删去。对剩下的所有状态排序后可以得出当 \(a\) 递增时,\(b\) 对应递减。为了满足这个条件,对于 \(l\) 的同一个 \((a,i)\),我们肯定想要让 \((j,b)\) 中的 \(b\) 尽可能小,就是要找到最后一个满足条件的状态,可以简单用双指针实现。并且可知如果左右的合法状态数分别为 \(x,y\),那么最后合并出的合法状态数为 \(2\min(x,y)\) 个,时间复杂度是 \(O(x+y)\),容易推出总复杂度为 \(O(n\log n)\)

CF1446D2. Frequency Problem (Hard Version)

考虑根号分治,对于最大出现次数 \(\le\sqrt{n}\) 的序列,我们显然可以枚举最大出现次数后用双指针维护出最长的合法区间,这样的复杂度是 \(O(n\sqrt{n})\) 的。

而对于最大出现次数 \(>\sqrt{n}\) 的序列,考虑其中的两个出现次数最多的元素一定是在整个序列中出现次数 \(>\sqrt{n}\) 的数,而这样的数最多有 \(\sqrt{n}\) 个。接下来我们先证明一个引理:最优解的众数中一定包含全局众数。这个不难用反证法证明,即如果不包含全局众数,那么可以尝试拓展边界使得全局众数成为当前区间的众数,容易发现这可以实现,并且这个区间比之前不包含全局众数的区间更优。那么我们可以枚举另一种数,如果我们确定了哪两个数出现次数最多,将一种视为 \(-1\),另一种视为 \(1\),其余视为 \(0\),则问题转化为找出最长的和为 \(0\) 的区间,利用前缀和可以做到 \(O(n)\) 单次求解,总复杂度是 \(O(n\sqrt{n})\) 的。虽然我们找出的区间中可能存在另一个数的长度更长,但这说明选择这个数时我们的答案要更优,证明和上面的过程类似,于是正确性没有问题。

CF765F. Souvenirs

考虑莫队,普通的莫队显然较难实现这道题,因为我们需要维护区间中的 \(\min\),发现在往区间中加数时答案是简单的,将答案和新加入的点与其前驱后继的距离取 \(\min\) 即可,反观删除点是我们无法快速确定答案,因此考虑回滚莫队。一个 naive 的想法是用 set 维护当前区间的数。对于在同一个块的询问可以直接暴力求解,而对于块 \([L,R]\) 满足 \(L\le l\le R<r\) 的询问 \((l,r)\),对这些询问按照右端点排序。我们先将 set 的内容调至 \([R+1,R]\),然后右端点不断扩充,左端点适配即可,容易做到 \(O(n\sqrt{m}\log n)\)

但是这样做还是太慢了,我们换一个更高速的维护前驱后继的数据结构:链表。现在的问题就是链表在增加数的时候复杂度依旧为 \(O(\log n)\),然而考虑我们手写链表有一个好处:删除一个点后会保留其在删除时的前驱后继信息。这说明我们可以做到 \(O(1)\) 撤销上一次的操作,于是我们可以改写上面的操作:我们对于块 \([L,R]\) 满足 \(L\le l\le R<r\) 的询问 \((l,r)\),对这些询问按照右端点排序。然后进行以下操作:

  1. 我们先将list 维护 \([L,n]\) 的内容删除到 \([L,R]\),接着不断撤销删除操作适配右端点,在适配完成后,将左端点删到 \(R+1\) 后撤销操作到 \(l\),这样在最后一步中,我们可以获知在数列中已有 \([R+1,r]\) 中的数时 \([l,R]\) 中的所有数对答案的贡献,最后将左端点撤销到 \(L\)
  2. 我们再维护一个 list,先将初始时维护的 \([R+1,n]\) 的内容删除到 \([R+1,R]\),然后不断撤销操作适配右端点,这样我们可以获知 \([R+1,r]\) 中的数对答案的贡献。

上面两者取 \(\min\) 即可获知答案。对于 \(L\le l\le r\le R\) 的询问,可以先将第一步中的 \([L,n]\) 删到 \([L,L-1]\),之后按照相同的思路,先撤销适配右端点,删除左端点到 \(r+1\) 后撤销统计贡献,没有额外的复杂度,可以做到 \(O(n\sqrt{m})\)

GYM103371M. Yet Another Range Query Problem

考虑对询问离线扫描线,每次移动右端点都会导致左端点处对应答案的值发生改变,我们考虑快速维护所有左端点的值。设 \(A_p=\max\limits_{i=p}^ra_i,B_p=\min\limits_{i=p}^ra_i\),考虑右端点移动造成的后果就是一段后缀最大值会变大或一段后缀最小值会变小,这显然可以通过单调栈做到 \(O(n)\) 求出所有值改变的区间然后进行区间加减操作,并且最多只有 \(O(n)\) 个区间需要操作。考虑到 \(\sum\limits_{i=l}^{r}\sum\limits_{j=s}^ef(i,j)=\sum\limits_{i=l}^r\sum\limits_{j=1}^ef(i,j)-\sum\limits_{i=l}^r\sum\limits_{j=1}^{s-1}f(i,j)\),于是我们可以对询问进行差分,对每一个询问,我们看出这本质是 \([l,r]\) 在右端点在 \([1,e]\) 之间的历史版本和,于是考虑维护历史版本和。用矩阵维护历史版本和 \(C\),首先考虑维护的向量:

\[\begin{bmatrix}\sum A&\sum B&\sum AB&\sum C&\text{len}\end{bmatrix} \]

然后考虑 \(A\) 区间加 \(k\)\(B\) 区间加 \(k\) 和更新版本和的转移矩阵(考虑实际意义后显然):

\[\begin{bmatrix}1&0&0&0&0\\0&1&k&0&0\\0&0&1&0&0\\0&0&0&1&0\\k&0&0&0&1 \end{bmatrix}\begin{bmatrix}1&0&k&0&0\\0&1&0&0&0\\0&0&1&0&0\\0&0&0&1&0\\0&k&0&0&1 \end{bmatrix}\begin{bmatrix}1&0&0&0&0\\0&1&0&0&0\\0&0&1&1&0\\0&0&0&1&0\\0&0&0&0&1 \end{bmatrix} \]

结果是只有 \(9\) 个位置需要维护:

\[\begin{bmatrix}1&0&A&B&0\\0&1&C&D&0\\0&0&1&E&0\\0&0&0&1&0\\F&G&H&I&1 \end{bmatrix} \]

我们暴力维护这九个位置的值后转移即可。同时在线段树上维护区间的向量,只需要进行区间操作即可,复杂度是 \(O(n\log n)\) 的。

CF643G. Choosing Ads

假设区间总长度为 \(L\),那么题目要求找到 \(c\ge \dfrac{np}{100}\) 的所有点,考虑因为可以输出错解,所以我们可以简单放宽限制,即考虑 \(c\ge\dfrac{np}{100}=\dfrac{n}{\frac{100}{p}}\ge\dfrac{n}{\lfloor\frac{100}{p}\rfloor+1}\)。令 \(k=\lfloor\dfrac{100}{p}\rfloor+1\),那么我们每次选出 \(k\)互不相同的数抵消,最后剩下的就可能满足条件。证明考虑有没有选到目标数,显然有 \(c\ge \dfrac{n}{k}\Rightarrow c>c-1\ge \dfrac{n-k}{k}\),于是不管是否选到目标数,式子依旧成立。(特别的,当 \(k=2\) 时,上述过程被称作摩尔投票,用来寻找绝对众数,即出现次数 \(\ge\dfrac{n}{2}\) 的数。)

于是我们维护线段树,合并的时候将左右区间的可能解合并后做相同处理即可。合并完后区间最多有 \(2k-2\) 种数,每次选出 \(k\) 个至少抵消掉一个,因此单次合并的复杂度是 \(O(k^2)\) 的,结合线段树复杂度为 \(O(mk^2\log n)\)

CF1209G2. Into Blocks (hard version)

考虑没有修改时怎么做,不难考虑贪心。对于每一个颜色求出其覆盖的区间 \([L,R)\),对所有这些区间加 \(1\),不修改的颜色即为相邻两个 \(0\) 之间出现次数最多的颜色。

接下来考虑修改,考虑维护答案需要什么:\(0\) 的位置、相邻 \(0\) 之间的最大值的和。直接维护当然是很难的,我们先将所有线段的贡献放到其左端点处,考虑到因为区间是左闭右开的,那么 \(n\) 这个位置上一定是 \(0\),并且每次查询都是全局的,所以我们可以大胆维护所有最小值之间的答案。考虑维护 \(\text{mn},\text{mx},\text{lmx},\text{rmx}\) 分别表示区间覆盖次数的最小值,区间所有最小值之间的最大颜色出现次数的和,左端点所在区间最大颜色出现次数和右端点所在区间最大颜色出现次数。这样向上传递的时候只需要考虑左右两个区间的最小值关系即可。对于修改操作来讲相当于进行区间修改覆盖次数和单点修改颜色出现次数,直接用线段树维护即可做到 \(O(n\log n)\)

CF1270H. Number of Components

Solution1

一个 naive 的想法是考虑在一个数后比它大的数都没有意义,我们将这个内容限制成在序列上必须连续,那么我们会得到一些连续递增区间,这些区间对应的值域分别为 \([L,R]\)。那么对于我们得到的这么多线段,如果存在两个线段值域有交,那么它们肯定在一个连通块,于是我们可以考虑维护一下这些线段的并快速求出有多少个线段的连通块,这个内容可以用经典 trick:建两棵线段树,在线段树上分别进行 \([L,R)\) 区间和 \([L,R]\) 区间加 \(1\),同时维护有多少个位置是 \(0\),两者作差就是答案。维护位置是 \(0\) 的个数可以考虑每个位置的值都 \(\ge 0\),因此维护最小值的个数即可。

对于单点修改,我们考虑用 set 维护当前的所有连续递增区间在序列上对应的区间,这样我们可以对当前点在所在区间的哪个位置进行讨论(这个点单独是线段、在线段开头、在线段结尾、在线段中间),然后对于每一种情况对其修改后对连续递增区间的影响都进行分类讨论即可。虽然这样做很复杂,但复杂度依然是 \(O(n\log n)\) 的。

Solution2

考虑一个相似的思路,称一个连通块内最大的点为 \(P\),那么这个连通块前面的所有连通块内的数一定都 \(>P\),而从这个连通块以后所有的数都一定 \(\le P\),不难看出这个条件是 \(P\) 为连通块内最大的点的充要条件。于是可以考虑对每一个 \(P\) 维护一个序列,每个位置为 \(1\) 表示这个位置 \(>P\),为 \(0\) 表示这个位置 \(\le P\),从而符合条件的序列一定形如 \(11\cdots100\cdots0\)。考虑维护序列中 \(10,01\) 的个数,对于相邻的两个数 \(A,B\),显然只有在 \([\min(A,B),\max(A,B))\) 中的数才会产生贡献,进行区间加即可,最后统计序列中 \(10,01\) 的个数为 \(1\) 的数的个数。因为 \(a_0=+\infty\),所以第一位一定为 \(1\),因此个数为 \(1\) 当且仅当前缀为 \(1\) 后缀为 \(0\)。然而区间中的某些数可能不在序列中,因此只有序列中的数才会产生贡献。维护的时候考虑每一个数对应的 \(10,01\) 的个数一定 \(\ge 1\),因此维护最小值的个数即可。复杂度是 \(O(n\log n)\) 的。

CF1558F. Strange Sort

首先原问题直接做很难,考虑缩域,转而对 \(01\) 序列进行这种操作。不难看出对于所有 \(x\in[1,n]\),令 \(a_i\leftarrow[a_i>x]\),并计算新的 \(a\) 排序所用的次数,最大值即为答案。问题转化成如何快速求一个 \(01\) 序列排序所用的次数。

考虑设 \(f_i\) 表示第 \(i\)\(0\) 到达对应位置的时间,如果在过程中和第 \(i-1\)\(0\) 相遇,那么在延迟 \(1\) 次操作后会和第 \(i-1\)\(0\) 进行一样的移动,步数为 \(f_{i-1}+1\),否则考虑前面有 \(k_i\)\(1\),则一定只会一一和这些数交换,但第一次交换可能无用,设 \(p_i\) 为第 \(i\)\(0\) 的位置,则步数为 \(k_i+(p_i\bmod 2)\),于是有

\[f_i=\left\{\begin{aligned}&0,&&p_i=i;\\&\max(f_{i-1}+1,k_i+(p_i\bmod 2)),&&\text{otherwise}.\end{aligned}\right. \]

考虑若总共有 \(m\)\(0\),我们其实只关心 \(f_m\) 的值,将上面的式子暴力展开,设 \(L\) 为第一个 \(p_L\ne L\) 的位置,那么有

\[f_m=\max_{i=L}^{m}(k_i+(p_i\bmod 2)+(m-i)) \]

将上面的式子放到序列上,改 \(k_i\) 定义为第 \(i\)\(0\) 前面有多少个 \(0\),有

\[f_m=\max_{i=L}^{m}([a_i=0](i-k_i+(i\bmod 2)+(m-k_i)))=\max_{i=L}^m([a_i=0](i+(i\bmod 2)-2k_i))+m \]

考虑用线段树维护这个内容,每次将一个 \(1\) 改为 \(0\),对 \(k_i\) 的影响是一个后缀,区间修改即可。注意只有值为 \(0\) 的地方才有贡献,复杂度为 \(O(n\log n)\)

CF1034D. Intervals of Intervals

先考虑如何快速求出所有 \([l,r]\) 对应的权值,我们按顺序枚举线段 \([a,b]\),当固定 \(r\) 时,我们尝试求出所有左端点 \(l\) 对应的答案,考虑每个位置 \(p\in[a,b]\) 最后被覆盖的线段 \(\text{lst}_p\)(没有则为 \(0\)),那么这个位置对左端点 \(l\in[\text{lst}_p+1,r]\) 的所有位置有 \(1\) 的贡献。考虑用 set 维护一段连续的 \(\text{lst}\) 相同的区间,那么可以做到 \(O(n\log n)\) 求得对于每个 \(r\) 需要修改的区间,然后即可求解。

回到本题,我们肯定想选择权值前 \(k\) 大的区间,可以考虑求出第 \(k\) 大的区间的权值 \(v\),这个可以用二分实现。最后利用权值 \(\ge v\) 的区间个数和权值和可以简单算出答案,问题是如何求出有多少个区间的权值 \(\ge v\)。首先需要发现一些性质:设 \(f(l,r)\)\([l,r]\) 的权值,那么 \(f(l,r)\ge f(l+1,r),f(l,r)\ge f(l,r-1)\),也就是对于固定的右端点 \(r\),有 \(f(1,r)\ge f(2,r)\ge\cdots\ge f(r,r)\),考虑这里面最后一个 \(\ge v\) 的区间 \(f(l,r)\),记 \(L(r)=l\),那么答案即为 \(\sum_{r=1}^{n}L(r)\),因而只需要快速求出 \(L\) 即可。显然可以预处理出在 \(r\) 时的所有修改操作,在线段树上操作后二分即可,但这样复杂度是 \(O(n\log n\log V)\) 的。考虑到 \(f(l,r)\ge f(l,r-1)\),因此有 \(L(1)\le L(2)\le \cdots\le L(n)\),于是可以利用差分数组和双指针简单求解出 \(L\),这样复杂度降到 \(O(n\log V)\),可以通过。

UOJ671.【UNR #5】诡异操作

考虑两个修改操作都会让数字不断下降,我们选择更好维护的区间与维护标记。对于区间除我们没有好的办法维护,不如直接暴力,单次在线段树上的枚举复杂度不超过 \(O(n)\),判断不需要继续操作的位置后整体复杂度可以到 \(O(n\log V)\)。对于区间与我们考虑维护区间每一位上有多少个数字为 \(1\),这样可以做到 \(O(\log V)\) 进行区间与标记下放、合并信息、暴力除的修改,因此这样复杂度时 \(O((n\log V+q\log n)\log V)\),因为 \(\log V=128\),因此难以通过。

考虑一个想法,对于维护的 \(\log V\) 个信息,写成 \(\log V\times \log n\) 的二进制数表,我们转而维护 \(\log n\) 列上的内容。这样做可以让暴力除之后的修改只在第 \(0\) 列上操作,从而做到 \(O(1)\),标记下放、合并信息可以用二进制加法做到 \(O(\log n)\),因而总复杂度下降到 \(O(n\log V+q\log^2 n)\)。一个细节是关注暴力除后的合并信息,复杂度是 \(O(\sum \log)\) 的,考虑利用主定理求解 \(T(n)=2T(\dfrac{n}{2})+O(\log n)\) 得到 \(T(n)=O(n)\),那么复杂度也是 \(O(n\log V)\) 的。精细实现可以通过。

UOJ515.【UR #19】前进四

首先这题可以用前缀最值线段树维护简单做到 \(O(n\log^2n)\),然而难以通过。

考虑对所有操作离线下来,本来我们按时间轴的顺序处理操作,我们转而按照操作位置从大到小去处理操作,用线段树维护每个时刻当前的后缀最值,那么只需要对这个位置的操作划分出的若干个区间取 \(\min\) 并将取 \(\min\) 的位置对应的答案 \(+1\) 即可,使用吉司机线段树实现可以做到 \(O(n\log n)\)

UOJ681.【UR #22】月球列车

考虑拆位维护,就是考虑最终的结果每一位有多少个 \(1\)。对于 \(a_i+x\) 来说,第 \(i\) 位为 \(1\) 当且仅当 \(a_i\) 的第 \(i\) 位异或 \(x\) 的第 \(i\) 位异或 \(a_i+x\) 在第 \(i-1\) 位的进位的结果为 \(1\),特别的,第 \(-1\) 位的进位始终为 \(0\)。前两个内容很好维护,只考虑最后一个内容即可,而且不难看出会产生进位当且仅当 \(x\land(2^i-1)\ge 2^{i+1}-a_i\land(2^i-1)\),对 \(a_i\) 维护后面的内容,对每一位排序,然后在数组中二分即可,复杂度是 \(O(n\log n\log V)\) 的。

考虑优化,首先不难发现排序可以通过基数排序做到 \(O(n\log V)\),同理我们可以预处理在数组中二分的结果。具体的,我们考虑维护 \(c_{0/1,p,i}\) 表示第 \(p-1\) 位的排序结果的前 \(i\) 个数有多少个数的第 \(p\) 位为 \(0/1\),那么通过维护第 \(p-1\) 位进位的数的个数 \(\text{cur}\),根据单调性我们可以清楚地知道是后 \(\text{cur}\) 个数进位了,接着对上一位进位的和没进位的数分类讨论即可,那么可以做到 \(O(n\log V)\)

JOISC2023L. ビ太郎の旅

首先我们走到的景点一定是一段连续的区间。接着我们考虑每次转换方向都一定意味着下一个点的距离超过了当前两个端点景点之间的距离,也就是说下一次走到这个景点两个端点景点之间的距离会翻一倍,而这样的景点是拐点产生的,由此我们推断出最多存在 \(O(\log V)\) 个拐点,只需要能够快速找到这些拐点即可。

以找向右的拐点为例,现在我们走到了 \([L,R]\) 区间,考虑什么时候我们不会向右走而是会向左走,设一个 \(L_p\) 表示 \(p\) 如果不向右走到 \(p+1\) 最远能向左走到的位置,那么就是存在一个 \(p\) 使得 \(L_p<L\),此时我们一定从 \(p\) 走到 \(L-1\),也就是要找到 \(R\) 右边第一个 \(L_p<L\)\(p\),这个利用 ST 表+二分可以简单实现,向左的拐点同理。最后特判边界情况统计答案即可,实现可以用记忆化优化掉同一个景点为起点的答案,总复杂度是 \(O(n\log n\log V)\) 的。

JOISC2023K. 魚 2

考虑第 \(i\) 条鱼最多能吃的区间 \([L_i,R_i]\),就是不断向左向右扩展直到两边都无法进行扩展时的区间,也是最小的包含 \(i\) 且满足 \(\sum\limits_{p=L}^RA_p<\min(A_{L-1},A_{R+1})\) 的区间(令 \(A_0=A_{n+1}=+\infty\)),那么对于区间 \([l,r]\) 的答案就是在 \([l,r]\) 意义下扩展区间为 \([l,r]\) 的区间。

考虑我们需要进行单点修改,那么可以想到线段树,现在我们只需要考虑如何合并两个子树的信息。设 \([l,r]\) 的中点为 \(m\),即左右子树的对应区间为 \([l,m][m+1,r]\),考虑到如果一个点在其子树对应区间内无法吃掉一个端点,那么这个点是无意义的,因为没有办法依靠新加入的区间来扩展,那么不管大区间是什么扩展区间都不变,这启发我们在每个点上只维护能够吃掉左端点或右端点的扩展区间,并将所有扩展区间相同的点合并,维护扩展区间为 \([l,p]\)\([p,r]\) 的点有多少个,因为每个不一样的区间之间的权值和会至少变为 \(2\) 倍,因此最多会维护 \(O(\log V)\) 个区间。考虑合并区间,以左区间的 \([p,m]\) 扩展区间为例,我们考虑将其和 \([m+1,q]\) 进行匹配,具体的我们考虑对左右端点按照包含关系从小到大排序,我们对于一个初始区间不断左右扩展直到无法继续扩展,事实上只有右端点存在的前缀才会成为最终区间的右端点,左区间存在的后缀才会成为最终区间的左端点,于是可以用两个指针分别指代。此时显然所有 \([\ge p,m]\) 的区间都会扩展到这里,对所有区间的个数累加放到这个新区间即可,右端点同理,这样可以做到单次合并 \(O(\log V)\),依旧注意区间依旧需要是一个前缀或一个后缀,不合法的直接删去即可。对于扩展区间是整个区间的点我们特判出来单独维护即可,复杂度是 \(O((n+q\log n)\log V)\) 的。

总结起来就是我们维护每个区间的左右端点的权值、区间和、扩展区间为 \([l,r]\) 的点的个数和所有前缀、后缀扩展区间,对每一个扩展区间维护其所有元素的和、扩展区间是这个区间的点数和限制这个区间的位置的权值,维护这些东西就可以支持合并了。

CF1442D. Sum

直接做背包是 \(O(nk^2)\) 的,我们考虑发现一些性质。首先可以容易得到一个结论:最多只有一个序列没有选满。可以用反证法证明:假设有两个没有选满,比较两个序列的最后选择的值,设有 \(A_{1,p}<A_{2,q}\),于是可以考虑将 \(A_{1,p}\) 取消,进而选择 \(A_{2,q+1}\),由于递增的性质,这样一定不劣。并且由于递增,取消之后的 \(A_{1,p-1}\) 依旧小于 \(A_{2,q+1}\),这样重复操作直到一者选空或选满肯定更优,这样枚举哪个数组没有选满,剩下的数组就只用进行 \(01\) 背包了,总复杂度是 \(O(n^2k)\) 的。

现在考虑一个经典的分治思路:\(\text{Solve}(l,r)\) 表示正在求解 \([l,r]\) 区间,我们先将左区间 \([l,\text{mid}]\) 的所有数组放入背包中,然后调用 \(\text{Solve}(\text{mid}+1,r)\),接着还原背包,将右区间 \([\text{mid}+1,r]\) 的所有数组放入背包中,然后调用 \(\text{Solve}(l,\text{mid})\)。这样做的好处是当处理 \([l,r]\) 时,除这个区间外的所有数组都已经被放入了背包,这样我们只需要在 \([l,l]\) 处统计答案即可,复杂度分析 \(T(n)=2T(\dfrac{n}{2})+O(nk)\) 得到时间复杂度是 \(O(nk\log n)\) 的。

posted @ 2025-05-12 17:24  DycIsMyName  阅读(21)  评论(0)    收藏  举报