Ynoi
Ynoi
大战 Ynoi 系列,顺序是洛谷题号排序。
已经有标题但是还没写 sol 的是因为博主是个大鸽子。
P4690
区间赋值,区间数颜色。
考虑静态询问区间颜色总数的经典问题怎么做?定义 \(pre_i\) 表示 \(i\) 之前最后一个和 \(i\) 颜色相同的下标。
那么询问 \([l,r]\) 的答案为 \(\sum_{i=l}^r[pre_i<l]\)。使用二维数点可以以 \(O(n\log n)\) 的复杂度解决。
沿袭静态的做法,很容易做单点修改的问题:多带一维时间,离线使用 CDQ 分治即可以以 \(O(n\log^2 n)\) 的复杂度解决。
考虑使用 ODT 进行区间推平,颜色连续段修改时仅会改变开头位置的 \(pre\) 值,容易势能分析出总共仅有 \(O(q)\) 次单点改变 \(pre\) 值,离线使用 CDQ 分治即可。
时间复杂度 \(O(n\log^2n)\)。
P5065
给定一个长度为 \(n\) 的序列,单点修改,询问满足区间 or 不小于某个值的区间的最短可能长度。
一个经典的结论是固定一个端点,区间 or 只有 \(w=\log V\) 段单调的值。
维护每个位置 \(i\) 往后第 \(j\) 个 bit 出现的第一次下标 \(f_{i,j}\)。考虑 \(i\) 之后的 \(w\) 段不同的区间 or 可以通过排序 \((f_{i,j},j)\) 找到。对于 \(i\) 的升序 \((f_{i,j},j)\) 序列转移到 \(i-1\) 的序列仅需考虑对序列进行归并(重复的 \(j\) 只取较前者)。单次复杂度 \(O(w)\)。
考虑分块,设块长为 \(B\),考虑对于每个块维护 \(mx_i\) 表示块内长度为 \(i\) 的区间能取到的最大区间 or,维护块左端点在块内往后的 \((f_{l,j},j)\) 升序序列以及块右端点在块内往前的 \((f_{r,j},j)\) 降序序列。
对于修改,容易 \(O(Bw)\) 重构出块内维护信息。
对于查询,对于每个块内的区间可以 \(O(\log n)\) 找出答案,合并出前缀块右端点往左的信息与后缀块左端点往右的信息(复杂度 \(O(\dfrac n Bw)\)),枚举块间,双指针贡献答案。
取 \(B=\sqrt n\),总复杂度 \(O(n\sqrt n\log V)\)。
P5311
给定一棵大小为 \(n\) 的树,点有颜色,\(m\) 次询问保留编号在 \([l,r]\) 的点后,\(x\) 所在连通块颜色种类数。
考虑任意一个连通块在点分树上一定均在一个子树里(连通块中在点分树上深度最小的点)。
考虑对于一个询问找到 \(x\) 所在连通块在点分树里深度最小的点(记录点分治每一层每个点到重心的节点 \(mn,mx\))。
对于一个点可以放在二维平面的 \((mn_i,mx_i)\) 上,询问 \(x\geq l,y\leq r\) 的点的颜色种类数。
考虑按 \(mn\) 从大到小排,扫描线,维护每个颜色出现的 \(mx\) 最小值,使用树状数组。
总时间复杂度 \(O(n\log^2n)\)。
P5397
给定一个长度为 \(n\) 的序列,将所有值为 \(x\) 的位置修改为 \(y\),查询 \(x\) 和 \(y\) 的最近距离。
考虑根号分治。
若询问中两个颜色的集合大小(下文称颜色大小)均不超过 \(\sqrt n\),那么暴力归并可以得到答案。
否则对于一个大小超过 \(\sqrt n\) 的颜色,预处理其与其它所有颜色的答案。
若合并的两个颜色大小均不大于 \(\sqrt n\),直接合并,修改大块对小块的答案,如果合并后大小超过 \(\sqrt n\) 成为大块,那么 \(O(n)\) 预处理其与其他所有颜色的答案。
若合并的两个颜色大小均大于 \(\sqrt n\) 的,直接合并,暴力修改大块对小块的答案,复杂度 \(O(n)\)。
否则考虑小块和大块的合并,对于每个大块维护一个附属集合用于处理与其合并的小块,将小块与附属集合合并,转换成小块与小块合并的情况。
复杂度分析考虑大块之间的合并仅有 \(O(\sqrt n)\) 次,总复杂度 \(O(n\sqrt n)\)。
P5525
给定一个 \(n\) 个点的凸包,查询 \(m\) 个圆是否被凸包包含。
考虑判断一个圆心为 \(O\),半径为 \(r\) 的圆是否被凸包包含,等价于将凸包的所有边往里推 \(r\) 形成的新凸包中包含 \(O\)。我们考虑离线询问对于 \(r\) 扫描线,模拟凸包收缩的过程,平衡树维护凸包线段端点关于 \(r\) 的位置以及线段消失时间。
时间复杂度 \(O((n+m)\log n)\)。
P6106
平面上有 \(n\) 条线段,\(m\) 次询问一个边与坐标轴平行的矩形,问线段和矩形的交的长度和。
4-side 矩形太复杂,我们直接差分成 2-side,考虑到边界问题,我们分开考虑斜率为正和为负的线段,以两种不同的差分方法处理。
那么我们此时只需要考虑完整在 2-side 矩形内的线段贡献和在边界上的线段贡献。
前者直接一个二维数点就可以了,后者我们可以扫描线,平衡树维护线段相对位置。
时间复杂度 \(O(n\log n)\)。
P6109
有一个 \(n\times n\) 矩阵,初始全 0,有 \(m\) 次矩形加,\(q\) 次矩形最大值,修改均在查询后。
考虑扫描线,将修改差分成 \(x=l_1\) 区间加 \(v\),\(x=r_1+1\) 区间减 \(v\)。
那么 \(q\) 个询问的形式应该是 \(x\in [1,l_1-1]\) 的修改加入线段树,\(x\in [l_1,r_1]\) 的修改计入历史最大值,在 \(x=r_1\) 查询区间历史最大值。
考虑分治,每次处理所有跨越分治中心的询问。我们需要支持撤回区间加操作直到对历史最大值没有贡献的位置。
具体实现可以考虑做完需要撤回的区间加逆操作后,全局加一个足够大的偏移量 \(C\),使得历史最大值取到加偏移量后的当前量,查询时减去偏移量即为原值。
时间复杂度 \(O(n\log^2n+q\log n)\)。
P6578
单点修改,询问区间的子区间最大值 \(\leq x\) 的子区间个数。
静态怎么做?考虑询问离线,按 \(x\) 升序排列,按权值升序加入下标,使用线段树维护已加入的极长连续段(\(\dbinom{len} 2\) 和)。时间复杂度 \(O(n\log n)\)。
考虑单点修改怎么做。操作分块,每次处理 \(O(\sqrt n)\) 个询问和 \(O(\sqrt n)\) 个修改。
考虑沿袭静态问题的想法,按权值升序加入下标,将涉及修改的下标空着,按 \(x\) 升序排列询问,每个询问都将在其前的修改做一遍。
仍然可以使用线段树,不过带 \(\log n\),无法通过,修改 \(O(n\sqrt n\log n)\),查询 \(O(n\log n)\) 过于不平衡,考虑设计一个序列分块平衡复杂度。
序列分块里维护块前缀极长 1 段,后缀极长 1 段,块内除前后缀极长 1 段 \(\dbinom{len} 2\) 和,块内维护一个极长 1 段起点到终点的链表,修改时容易 \(O(1)\) 更改链表,查询时散块暴力,整块利用维护信息即可,每个询问后撤销修改。
总复杂度 \(O(n\sqrt n)\)。
P6780
给定一个长为 \(n\) 的序列 \(a\) 和常数 \(c\),\(m\) 次操作,支持单点修改,询问区间内子区间长度不超过 \(c\) 的最大子段和。
考虑按 \(c\) 对序列分块。
区间长度不超过 \(c\) 的区间要么完整包含在一个块里,要么仅跨越两个相邻块。
我们分别计算两种情况的贡献。
对于前者,我们考虑每个块维护一颗线段树维护最大子段和,容易支持单点修改。
再维护一颗下标为整块的线段树,询问时查询一个区间的整块信息。散块在每个块对应的线段树里查贡献即可。
这部分复杂度单 \(\log n\)。
跨块的情形我们可以再分成跨越两个整块和两边的两个散块和整块/散块和散块。
具体的,考虑左块长为 \(i\) 的后缀(记后缀和为 \(A_i\))和右块长为 \(j\) 的前缀(记前缀和为 \(B_j\))拼出的一个子段,满足 \(i+j\leq c\),可以贡献 \(A_i+B_j\)。
我们反转 \(B\) 数组,令 \(j=c-j'\),则\(i\leq j'\),这样的最大值容易线段树 pushup 时维护。
单点修改只是区间修改 \(A,B\) 数组。
带散块的情形只是多一个区间限制。
总复杂度 \(O(m\log n)\)。
P6781
给定一个长度为 \(n\) 的括号序列,每个位置是一个左括号或者右括号 \(a_i\),同时有一个权值 \(b_i\)。
需要支持单点修改,查询区间不匹配括号 \(b\) 的分治信息,区间交换。
因为有区间交换,查询分治信息,我们考虑使用平衡树维护序列。
考虑怎么处理查询,我们分别维护剩下的右括号以及左括号的 NAND 真值表和 \(b\) 最大值这两种分治信息。
容易发现 pushup 时,我们需要得到中间匹配完某一边剩下的括号,我们考虑进行单侧递归。问题模式形如查询一个节点的不匹配括号的右括号后缀或者左括号前缀。
两者较为对称,我们考虑前者。容易发现我们直接做可能需要递归到两边,但经过精细的讨论后,要递归到两边的情况一定有一边需要的值是这一层匹配完后剩下的全部信息。我们多记录该信息即可。
总时间复杂度 \(O(n\log n+m\log^2n)\)。
P7124
P7126
P7722
三个长为 \(n\) 的数组 \(a,b,c\),满足 \(a_i,b_i,c_i\in[1,n]\)。\(m\) 次操作,每次形如对数组 \(a\) 单点修改或者查询有多少个三元组 \(i,j,k\),满足 \(1\leq i<j<k\leq r\),且 \(b_{a_i}=a_j=c_{a_k}\)。
因为条件很奇怪,我们稍加修改另 \(b'_i=b_{a_i},c'_k=c_{a_k}\),那么修改变成单点修改 \(a,b',c'\),查询一个前缀的 \(b'_i=a_j=c'_k\) 三元组数量。
我们考虑对于一个颜色 \(v\),我们可以设计一个 DP 计算 \(b'_i=a_j=c'_k\) 的数量,具体的:
\(f_{p,0}\) 表示前 \(p\) 个位置有多少 \(b'_i=v\),\(f_{p,1}\) 表示前 \(p\) 个位置有多少个 \(b'_i=a_j=v\) 的二元组,\(f_{p,2}\) 表示前 \(p\) 个位置有多少 \(b'_i=a_j=c'_k=v\) 的三元组。显然转移是一个线性变换。
我们考虑对出现次数根号分治,所有全程出现次数不超过 \(B\) 的颜色我们每次单点修改时容易 \(O(B)\) 计算出改变量。对于出现次数超过 \(B\) 的颜色我们可以分块维护上述线性变换转移,平衡 \(O(m)\) 次单点修改和 \(O(q\times \dfrac{n+q}{B})\) 次前缀查询。
总复杂度 \(O(q\sqrt{n+q})\)。
P8265
P8336,[CTS2022] 燃烧的呐球
一个 \(n\) 个点的以 1 为根的有根树,有 \(m\) 个二元组 \((x_i,y_i)\)。定义 \(D(a,b)\) 表示在 \(a\) 子树里却不在 \(b\) 子树里的节点个数。
一张 \(m\) 个点的完全图,\(i,j\) 之间的边权为 \(D(x_i,x_j)+D(x_j,x_i)+D(y_i,y_j)+D(y_j,y_i)\),求最小生成树大小。
直接上 Boruvka。
分析一下 \(D(a,b)+D(b,a)\) 。当 \(a,b\) 没有祖先后代关系时,值为 \(siz_a+siz_b\),否则不妨设 \(a\) 是 \(b\) 的祖先,值为 \(siz_a-siz_b\)。
试图分类讨论 \(x_i,x_j\),\(y_i,y_j\) 两对点的位置关系,统计 Boruvka 每一轮中每个点异色的最小边。
具体的,对于 \(x_i,x_j\) 或者 \(y_i,y_j\) 没有祖先后代关系的情况比较容易,这里讨论前者,后者对称。
我们可以把 \(+siz_{x_i},+siz_{x_j}\) 挂到 \(y_i,y_j\) 上,对于一个固定的 \(y_i\),我们只需要查询其子树内异色的最小的 \(siz_{x_j}-siz_{y_j}\) 以及子树外异色的最小的 \(siz_{x_j}+siz_{y_j}\)。
我们可以通过维护最小次小换根 DP \(O(n)\) 的完成这个部分。
对于 \(x_i,x_j\),\(y_i,y_j\) 均有祖先后代关系的情况我们进行更细致的讨论。
-
\(x_i\) 是 \(x_j\) 的祖先,\(y_i\) 是 \(y_j\) 的祖先。
我们可以以 \(y_j\) 的 DFS序作为下标线段树合并,dfs 时遇到一个 \(x_j\) 我们在 \(y_j\) 的 DFS 序插入贡献。
遇到 \(x_i\) 时,查询 \(y_i\) 子树内的最小次小完成异色查询。
时间复杂度 \(O(m\log n)\)。 -
\(x_i\) 是 \(x_j\) 的祖先,\(y_j\) 是 \(y_i\) 的祖先。\(x,y\) 反过来对称。
DFS 到 \(y_j\) 时在 \(x_j\) 的 DFS 序插入贡献,到 \(y_i\) 时查子树 \(x_i\) 内的最小次小完成异色查询。
回退时撤销 \(x_j\) DFS 序上的信息即可。时间复杂度 \(O(m\log n)\)。
-
\(x_j\) 是 \(x_i\) 的祖先,\(y_j\) 是 \(y_i\) 的祖先。
延续情况 2 的想法。DFS 到 \(x_j\) 时在 \(y_j\) 插入贡献,到 \(x_i\) 时查 \(y_i\) 到根的链上的最小次小信息完成异色查询。
回退时撤销 \(y_j\) 贡献即可。
可以树剖线段树做到 \(O(m\log^2 n)\),或者全局平衡二叉树 \(O(m\log n)\)。
总复杂度取决于情况三的实现,\(O(n\log m+m\log^2n\log m)\) 或者 \(O(n\log m+m\log n\log m)\)。
P8337
给定一个长为 \(n\) 的数组 \(a\),\(q\) 次询问一个区间有多少子区间满足元素对异或封闭,强制在线。
一个经典的结论是一个元素集合 \(S\) 对异或封闭等价于 \(|S|\geq 2^k\),\(k\) 为集合 \(S\) 的线性基大小。
考虑扫描子区间的右端点,在线性基中加入元素采取经典的区间线性基处理(CF1100F),那么区间 \([l,r]\) 的线性基大小就是线性基内标记 \(\geq l\) 的元素个数。
我们将线性基内的标记排开,对于每个 \(k\) 都可能存在一段【有贡献的左端点区间】,一个较好的性质是对于同一个 \(k\),右端点增时有贡献的左端点区间做右端点不降。
对于一次询问 \([L,R]\),我们枚举 \(k\),只需要得到 \(r\leq R\) 的所有【有贡献的左端点区间】和 \([L,R]\) 的交的长度和。
狂暴差分预处理,容易 \(O(1)\) 计算。
总复杂度 \(O(n\log n)\)。
P8512
一个长为 \(m\) 的序列,初始全 0,有 \(n\) 个区间赋值操作。
\(q\) 次询问进行完一个区间的区间赋值操作后整个序列里所有数的和,询问独立。
询问离线扫描线,珂朵莉树维护每个位置最后一次被覆盖的操作编号,拿一个 BIT 维护以最后一次操作编号为下标的元素和。
单个询问就是查询最后一次覆盖编号不小于 \(l\) 的元素和。
时间复杂度 \(O(n\log n)\)。
P8526,[CTS2022] 普罗霍洛夫卡
给定一个长为 \(n\) 的序列 \(a\),\(m\) 次查询 \([l,r]\) 的所有子区间的不同颜色数异或和。
扫描线,问题转化为区间加,区间历史版本异或和。这个问题不好做 \(\text{poly}(\log n)\) 我们考虑分块。
为了将历史版本异或和和描述得具体,引入辅助数组 \(b\) 即历史版本和数组。
我们整理我们需要支持的操作:
- \(a_i\) 区间加 1。
- 所有 \(b_i\) 异或上 \(a_i\)。
- 查询 \(b_i\) 的区间异或和。
我们考虑对于每个块维护一个 tag 表示该块没有下放的 1 操作次数。2 操作可以描述为 \(b_i:=b_i\oplus(a_i+tag)\)。
注意到这样的一个操作序列 \(b'_i=b_i\oplus(a_i+x_1)\oplus(a_i+x_2)\oplus\cdots\oplus(a_i+x_k)\),满足 \(x_i\) 单调不降,而相同的 \(x_i\) 可以保留至多一个,所以操作序列的长度即 \(x_i\) 的值域。
我们考虑对于 \(j\in[0,\sqrt n]\),提前预处理出块内 \(\oplus_i(a_i+j)\) 的值,当 \(tag\leq \sqrt n\) 可以直接调用,\(tag>\sqrt n\) 时重构整块。
现在我们只需要对于重构一个块做到 \(O(T)\),整个问题可以做到 \(O(nT+n\sqrt n)\),我们考虑如何做到 \(O(\sqrt n)\) 重构。
具体的,对于重构一个块,我们需要计算出下放标记后的 \(a,b\),以及下放标记后的 \(\oplus_i(a_i+j),j\in[0,\sqrt n]\)。显然很容易得到所有新的 \(a\)。
我们剩下要解决的问题是 \(\oplus_i(a_i+j),j\in[0,\sqrt n]\) 以及 \(\oplus_i(x_i+a_j),\forall j\)。
两个问题的处理非常对称,我们这里考虑前者。
我们试图 \(O(\text{值域})\) 地解决问题。将 \((a_i+j)_k\) 拆成 \((a_i)_k\oplus(j)_k\oplus[a_i \text{ 的 0 到 k-1 位}+j\text{ 的 0 到 k-1 位}\geq 2^k]\)。前两者直接 \(\oplus_i a_i\oplus j\) 即可,考虑处理后一个艾弗森括号。
由于 \(j\) 不超过 \(\sqrt n\),当 \(2^{k-1}\gt \sqrt n\) 时,\(j\) 截取 \(0\) 到 \(k-1\) 位的值就是 \(j\)。对于最后一个 \(2^{k-1}\leq \sqrt n\) 的 \(k\),称其为 \(p\)。容易发现 \(k\gt p\) 中只有一个 \(a_i\) 连续为 1 的前缀艾弗森括号取 1。容易做到 \(O(\sqrt n)\)。
对于 \(k\leq p\) 的部分我们可以将 \(a_i\) 截取后放桶里递推每一种前缀对应的 \(a_i\) 个数,由于有贡献的 \(j\) 在截取的部分是个后缀,我们可以打标记最后再递推处理,复杂度 \(O(2^0+2^1+2^2+\cdots+2^{k-1})=O(\sqrt n)\)。
总复杂度 \(O(n\sqrt n)\)。
P8527
两个长度为 \(n\) 的序列 \(a,b\),初始 \(b\) 全 0。
\(m\) 次操作每次形如给定一个 \(a\) 上的区间,将其平移到 \(b\) 的某一段加到 \(b\) 上。
操作让我们想到了多项式乘法。
我们考虑分块,散块暴力加,整块打平移 tag。最终每个块会有若干个 tag,将他们写成多项式的形式,FFT 优化。
设块长为 \(B\),复杂度为 \(O(mB+\dfrac{n}{B}(n\log n+m))\),\(B\) 取 \(\sqrt n\) 复杂度为 \(O(m\sqrt n+n\sqrt n\log n)\)。
P8528
有一个排列 \(a\),和一个序列 \(b\),长度均为 \(n\)。\(m\) 次操作,【将 \(b_i\) 加上区间内在 \(i\) 之前且 \(a_j\leq a_i\) 的 \(j\) 数量】,【单点查询 \(b_i\)】。
将修改操作差分一下,对于 \(b_i\) 的贡献表示成 \([1,i]-[1,l-1]\) 对 \(i\) 的贡献。
前者考虑预处理,分块打 tag 就行了,我们考虑处理后者。
一个前缀对 \([l,r]\) 的贡献,考虑分块,处理出前 \(j\) 个块对 \(i\) 的贡献,同样分块打 tag。
考虑一个散块对 \([l,r]\) 的贡献,将问题放在 \((i,a_i)\) 的二维平面上考虑,形如 \(O(n\sqrt n)\) 个矩形加 \(O(n)\) 个单点查。
其等价于 \(O(n\sqrt n)\) 个单点加,\(O(n)\) 个左下角查。这里修改的性质不够好,我们不好使用二维分块。
考虑对 \(x\) 这一维 cdq 分治,问题变成单点加,区间查,容易平衡复杂度至 \(O(n\sqrt n)\)。
总复杂度 \(O(n\sqrt n\log n)\)。
P8530
一个 \(n\) 个点的二维平面,第 \(i\) 个点坐标为 \((i,p_i)\),\(p\) 为一个 \(n\) 阶排列,每个点有一个颜色,不同的颜色有权值。
\(m\) 次查询矩形内不同颜色权值和。
对横坐标莫队,问题变成 \(O(n\sqrt m)\) 次加删元素,\(O(m)\) 次查询区间不同颜色权值和。
以 \(l\) 为横坐标,\(r\) 为纵坐标建立新的坐标系,对于一个颜色出现的若干位置 \(x_k\),可以刻画成 \((x_k,x_k)\) 的左上角覆盖,利用前驱后继信息可以差分成 \((x_k,x_k,+1),(x_k,x_{k+1},-1)\) 单点加,矩形求和,的形式。
前驱后继查询如果直接使用 set 等数据结构得带 \(\log n\),考虑使用不增加莫队利用链表解决。
查询考虑二维分块。对横坐标纵坐标分别先分 \(n^{0.75}\),每个块内分 \(n^{0.5}\)。
那么一次矩形询问会变成 \(n^{0.25}\times n^{0.25}\) 个 \((n^{0.75}/n^{0.5},n^{0.75}/n^{0.5})\) 整块和横坐标 \(n^{0.5}\) 的散块和纵坐标 \(n^{0.5}\) 的散块。
基于单点加的特殊形式,对于同一个横坐标,最多只有两个纵坐标有单点加,对于同一个纵坐标,也最多只有两个横坐标有单点加。那么散块我们暴力查询即可。故查询复杂度 \(O(n^{0.5})\)。
修改仅需要 \(O(1)\) 修改单点所在块信息。
总复杂度 \(O(n\sqrt m+m\sqrt n)\)。