小清新数据结构

小清新数据结构

很小清新的数据结构题,主要是线段树和树状数组。

CF840D Destiny

题意:求区间内是否存在出现次数严格大于 \(\dfrac{r-l+1}{k}\) 的数。

来自Alex_Wei老师的神仙思路:设\(d\)为严格大于\(\dfrac{r-l+1}{k}\)的最小数,那么如果一个数\(x\)至少出现了\(d\)次,就得满足一定出现在\(1,1+d,1+2d\cdots\)小的数中,那么直接判断这些数有没有满足条件的就可以了。

CF1030F Putting Boxes Together

题意:单点修,求 \(\min\limits_{x=1}^n\{\sum\limits_{i=l}^rw_i\times (|a_x-a_i|+|x-i|)\}\)

思路:对于每个询问,首先肯定要找到使得答案最小的 \(x\)。我们考虑当前的 \(x\) 变成 \(x+1\) 时答案的改变量,发现就是 \((a_{x+1}-a_x)\times(\sum\limits_{i=l}^xw_i-\sum\limits_{i=x+1}^rw_i)\),因此 \(x\) 取到 \([l,r]\) 的带权中位数时最优,即最小的满足 \(\sum\limits_{i=l}^x w_i\geqslant\sum\limits_{i=x+1}^rw_i\)\(x\)。于是就可以直接用线段树二分的话就是 \(O(\log n)\) 的。

CF1114F Please, another Queries on Array?

题意:区间乘,求区间积的 \(\varphi\)

思路:因为只有62个质数,因此可以用一个long long存下来区间内出现过的质数,然后维护区间乘、区间质数的标记就行了。

CF1149C Tree Generator™

思路:神秘括号序列题

有一个结论,树上\(x,y\)之间的距离是括号序列中这一段消去匹配的括号之后剩下的括号数量,因为每一段合法的括号序列对应走完了一个点及其子树,这样对答案没有贡献。即有\(dep_x-dep_{LCA(x,y)}\)个右括号,左括号同理,因此设\(f(l,r)\)\([l,r]\)消去匹配括号后的长度,那么答案即为\(\max\limits_{1\leqslant l\leqslant r\leqslant 2(n-1)}f(l,r)\)

\(f(l,r)\)不好维护,我们考虑用另一种方式来刻画括号数。我们把左括号当做\(+1\),右括号当做\(-1\),记前缀和为\(s_i\),那么这个区间消完后的右括号数为\(x=-\min s_i\),而剩下的就是\(y\)了,即设\(s_k=-x\),那么这个区间就被分成了\([l,k],[k+1,r]\)两段,答案就是\(sum(k+1,r)-sum(l,k)\)

我们考虑直接用线段树维护。对于每个节点,维护8个值,表示和、答案,左右端点开始的最大、最小前缀和、答案,就可以直接维护了。

CF1373G Pawns

题意:\(n \times n\) 的棋盘上,第 \(k\) 行是特殊行,\((x,y)\) 表示第 \(x\) 行,第 \(y\) 列,这个位置的兵可以走到 \((x-1,y+1),(x,y+1),(x+1,y+1)\)。每个兵都要走到第 \(k\) 行,不能有两个兵重合,可以在棋盘后添加若干列让每个兵都能走到第 \(k\)行。初始状态没有兵在棋盘上,现在有 \(m\) 个操作,将 \((x,y)\) 的状态改变,有兵则拿掉,没兵则添加。求每次操作后求需要最少要添加几列。

思路:设棋子\(i\)最终放在位置\(p_i\),那么有\(p_i\geqslant y_i+|x_i-k|\),于是设\(cnt_i\)表示有多少\(x_i+|y_i-k|\geqslant i\),那么答案就是\(\max{cnt_i+i-1}\),直接用线段树维护就可以了。

CF1422F Boring Queries

题意:在线区间 \(\text{lcm}\)

自己的思路:按照区间\(\varphi\)一样,在一个数的每一个质因子的幂次在上一个出现的位置去掉贡献。

正解思路:有基于根号分治的做法(官方题解),也可以类似我的思路直接用主席树,不过空间是\(O(n\log^2n)\)

P3960 [NOIP2017 提高组] 列队

思路:来写列队了。

之前用平衡树写过一遍,至今还没调出来,于是决定写树状数组做法。

其实核心思路很简单,就是维护出最后一列,我们需要维护单点删和查第 \(k\) 小,可以用树状数组上二分解决。然后就是每一行,其实维护方式也差不多,就是在第 \(m\) 列的操作要特殊处理。

P4108 [HEOI2015] 公约数数列

题意:单点修,求最小的整数 \(p\) 满足 \(\gcd(a_0, a_1, \cdots, a_p) \times \operatorname{xor}(a_0, a_1, \cdots, a_p) = x\)。其中 \(\operatorname{xor}(a_0, a_1, \cdots, a_p)\) 代表 \(a_0, a_1, \cdots, a_p\) 的异或和,\(\gcd\) 表示最大公约数。

思路:深感自己的弱小。

首先,本质不同的前缀 \(\gcd\) 只有\(O(\log v)\)个,可以枚举前缀gcd,然后问题就变成了区间是否有一个位置的前缀异或和为\(x\),但是这题待修,不能用log数据结构,于是考虑用分块。对于每一块,维护每个数到块头的异或和,然后排序,修改时整块打tag,散块暴力重构。

查询时,如果一个块内的前缀 \(\gcd\) 有变化,那么就暴力查,因为这样的块不会超过\(O(\log v)\)个,否则就二分看是否有答案。

P4585 [FJOI2015] 火星商店问题

题意:求出在标号和时间的两维限制下的 \(x\oplus(\text{a big bunch of numbers})\) 的最大值。

思路:我会整体二分!

不如线段树套 01Trie。

P4891 序列

题意:给出数组 \(a,b,c\)\(c_i=\max(a_i,b_i)\),求 \(\prod\limits_{i=1}^{n}\min(b_i,c_i)\),会把 \(a,b\) 变大。

思路:好像直接暴力修改,然后如果\(c\)值不会变就结束,可以直接过。

好吧,暴力其实过不了。

正解就是势能分析,可以证明如果在线段树上暴力修改然后在整个区间可以一起修改时就打tag的复杂度是对的。

需要维护区间答案、区间\(c\)的最小值,区间大于\(c\)\(b\)的最小值,区间\(c<b\)的个数,区间\(c\geqslant b\)\(b\)的积,区间覆盖标记。

P6240 好吃的题目

题意:区间 01 背包问题。

思路:区间 01 背包问题。

如果直接用线段树,那么每个询问需要\(O(\log n)\)次背包合并,复杂度为\(O(t\log n)\),明显过不去,于是考虑猫树分治,这样就可过了。

CF938G Shortest Path Queries

题意:给出一个连通带权无向图,边有边权,要求支持 \(q\) 个操作:

\(1\) \(x\) \(y\) \(d\) 在原图中加入一条 \(x\)\(y\) 权值为\(b\) 的边

\(2\) \(x\) \(y\) 把图中 \(x\)\(y\) 的边删掉

\(3\) \(x\) \(y\) 表示询问 \(x\)\(y\) 的异或最短路

保证任意操作后当前状态下的图连通无重边自环且操作均合法

思路:线段树分治+线性基,关键点在于如果形成了环就将环的异或值加入线性基。

CF992E Nastya and King-Shamans

题意:给定一个序列 \(a_i\) ,记其前缀和序列为 \(s_i\) ,有 \(q\) 个询问,每次单点修改,询问是否存在一个 \(i\) 满足 \(a_i=s_{i-1}\) ,有多解输出任意一个,无解输出 \(-1\)

思路:观察满足条件的数的个数。在不考虑 \(0\) 的情况下,如果一个 \(a_i\) 满足条件,那么对于一个比他小的满足条件的数 \(a_j\),一定会有 \(a_j=s_{j-1}\),而 \(s_{j-1}+a_j=s_j\leqslant s_{i-1}=a_i\),这就说明 \(2\times a_j\leqslant a_i\),因此满足条件的数至多有 \(O(\log V)\) 个。如果考虑 \(0\),那么任意一个在全零前缀里的数都满足条件,随便选一个就可以了。

因此,我们就可以暴力来找可能满足条件的数,即不断地找区间内最大的满足 \(s_{i-1}\leqslant a_i\)\(i\)。每次最大的可能做贡献的 \(i\) 需要满足 \(s_{i-1}\leqslant a_i\),而又有 \(s_{i-1}+a_i\leqslant\sum a\),因此有\(2s_{i-1}\leqslant \sum a\),于是就每次二分找出最大的满足 \(2s_{i-1}\leqslant \sum a\)\(i\),常规做法是线段树上二分。而在本题中这个区间是一段前缀,可以直接在树状数组上二分,非常好写。

CF633G Yash And Trees

题意:一棵树两种操作,第一种是某一节点子树所有值+v,第二种问子树中节点模m出现了多少种m以内的质数。

思路:CF少有的暴力题。

直接上线段树,每个节点维护长为 \(m\) 的bitset 即可。

P3793 由乃救爷爷

题意:\(\text{O(n)-O(1)RMQ}\)

思路:首先考虑分块,块长为\(\log n\),然后块间建ST表,建ST表的复杂度是\(O(\dfrac{n}{\log n}\log(\dfrac{n}{\log n}))=O(n)\),再维护每个点到块头的前缀最大值,到块末的后缀最大值,询问的复杂度是\(O(1)\)的。这样就有一个期望\(O(n)-O(1)\)的RMQ了。

怎么变成严格\(O(1)\)呢?我们考虑对每个点求出从块头到它的单调栈,这样单调栈中第一个\(pos\geqslant l\)\(pos\)就是\([l,r]\)的最大值的位置,而因为每个块长度都是\(\log n\),因此可以用一个数来表示每个数是不是在单调栈里,而查询时就是找最低位的1的位置,这样就可以做到\(O(1)\)了。

P8037 [COCI2015-2016#7] Prokletnik

题意:神仙题。

因为一共只有两种情况,于是可以先钦定左边是最小值,右边是最大值,然后反过来算一遍。

感觉不好做在线,考虑离线扫描线。假设当前考虑的右端点是 \(r\),我们的目标是对于每个 \(l\) 维护最长的满足条件的区间长度。

首先要找到 \(r\) 左侧第一个大于 \(a_r\) 的位置 \(L\),那么 \(r\) 能更新的位置一定在 \((L,r]\) 中。其次对于左端点 \(l\),要满足 \((l+1,r]\) 中不存在比 \(a_l\) 小的数,然后把这个点的答案更新为 \(r-l+1\)

具体地,我们维护两个单调栈,一个用来维护每个位置左边第一个比它的大的位置,一个不降的单调栈来维护每个位置是否能被更新。每一个在单调栈中被 \(r\) 弹出的位置就代表着 \(r\) 是第一个小于它的位置,这个位置之后就不能再更新了,于是可以记录这个点的贡献,加入不能再更新的部分。对于查询,我们查两个部分的贡献的最大值即可。

可以用树状数组实现。非常厉害。

一个树状数组维护不能再被更新的位置的最长满足条件的区间长度,另一个维护当前单调栈中一段前缀中的点作为左端点的满足条件的区间的最大右端点。

CF1379F1 Chess Strikes Back (easy version)

题意:给出一个 \(2n\times2m\) 的黑白交错棋盘,其中格子 \((1,1)\) 为白色。每次拿走一个白格,问能否在剩下的白格中放入 \(n\times m\) 个国王使得它们不互相攻击。

思路:很好玩的题目。

把每 \(2\times 2\) 的矩阵看成一个小矩阵,于是就要在每个小矩阵中放一个国王。

对于一个小矩阵,有 4 种情况:

  1. 没有限制
  2. 只有右下角能放
  3. 只有左上角能放
  4. 左上角右下角能放

这时无解的充要条件是 存在一个只有左上角能放的点在只有右下角能放的点的右下部分

于是可以对行建立线段树,维护最左侧的只有右下角可以放的点和最右侧的只有左上角可以放的点,这样就可以判断了。

CF1401F Reverse and Swap

给您一个长度为 \(2^n\) 的数组 \(a\),您现在需要处理 \(q\) 个询问,每个询问是以下 4 种类型之一:

  1. \(Replace(x, k)\)\(a_x\) 修改为 \(k\)
  2. \(Reverse(k)\) 对于每一个 \(i(i\ge 1)\) ,把区间 \([(i-1)\cdot 2^k+1, i\cdot 2^k]\) 的元素翻转;
  3. \(Swap(k)\) 对于每一个 \(i(i\ge 1)\) ,交换区间 \([(2i-2)\cdot2^k+1,(2i-1)\cdot2^k]\)\([(2i-1)\cdot2^k+1,2i\cdot2^k]\) 的所有元素;
  4. \(Sum(l,r)\) 输出区间 \([l,r]\) 中所有元素的和。

思路:显然可以想到建线段树。

对于 2,3 操作,我们要操作的恰好是第 \(k\) 层的节点,对于 4 操作,发现要操作的就是 \(0\sim k\) 的节点,给每层打标记即可。

CF1787G Colorful Tree Again

题意:给定一棵有 \(n\) 个节点的树,边有边权和颜色。每个点有被摧毁和不被摧毁两种状态,初始所有点都没被摧毁。

一条简单路径指图中没有重复节点的路径。简单路径的长度定义为路径上所有边的边权之和。

定义一条简单路径是好的,当且仅当路径仅有某一种颜色 \(c\) 构成,且所有颜色为 \(c\) 的边都在这条简单路径里,且路径上所有节点都没被摧毁。

你需要处理两种操作:

  1. 摧毁一个节点;
  2. 修复一个节点。

每个操作之后,你都需要输出最长的好的路径长度。

思路:引入一个叫 bdfs 序的东西。

bdfs 序就是先加入根节点,然后加入所有儿子,这样可以保证儿子的编号连续,以及子树中除了根节点编号连续。

首先,判断一个颜色是否合法比较容易,而且和后面的做法关系不大,可以先处理好。

接着我们相当于是要试试维护一种颜色还剩的点数,而且最好是和点直接联系,那么根据 bdfs 序的性质,我们可以把和父亲边颜色一样的边放一起,即对颜色重标号,使得要么和父亲边的标号一样,要么就是连续的区间,这样我们要做的就是区间修改和查最大值等于 0 的位置的最大权值,可以用线段树维护。

CF1797E Li Hua and Array

题意:给定一个长度为 \(1\le n\le 10^5\) 的数列,\(1\le a_i\le 5\times 10^6\)

\(m\le 10^5\) 次操作 o l r

  1. \(o=1\) 时, \(\forall\ l\le i\le r\)\(a_i\gets\varphi(a_i)\)
  2. \(o=2\) 时,找出使 \(a_l=a_{l+1}\dots=a_r\) 的最小变化。在每次变化中,选择一个 \(x\in[l,r],a_i\gets \varphi(a_i)\)。修改独立。

对于每个操作 \(2\) 输出最小答案。

思路:把一个数不断求 \(\varphi\) 的过程列出来,可以把题目转化为:有 \(n\) 个长度不超过 \(\log V\) 的序列,有区间删掉末位和求区间 LCP。

对于删掉末位的操作,可以用并查集维护每个位置下一个非 1 位置,然后暴力修改。

对于查询操作,可以看成是静态的,于是先预处理出 \(nxt_{k,i}\) 表示 \([i,nxt_{k,i}]\) 在第 \(k\) 位相等,查询时枚举 \(k\),上界是区间内所有序列的长度,这需要用线段树维护。

P2056 [ZJOI2007] 捉迷藏

题意:树上每个点有颜色 0/1,每次反转一个点的状态,求颜色为 1 的点之间的最大距离。

思路:括号序列!

把一棵树的括号序列求出来后,我们发现两点的距离就是这两点间的括号串去掉匹配后剩下的括号数。

于是就可以用线段树维护,每个节点维护当前区间的答案,左括号数,右括号数,前后缀最大的左右括号数量差、数量和,就可以合并了。

P7735 [NOI2021] 轻重边

题意:给一棵树,每条边可能是轻边或者是重边,有两种操作,一是把与一条路径上的点相连的路径变为轻边,然后把路径上的边变成重边,二是查询一条路径上有多少条重边。

思路:直接做很难,考虑怎么转化成更好做的形式。我们考虑把第一种操作变成给点染色,每一次操作就染成新的颜色,那么一条边两端颜色相同就是重边,否则就是轻边。

这个就可以直接用树剖维护了,相当于是在线段树上维护相同颜色连续段数及长度。不过在查询时合并处细节较多。

复杂度 \(O(m\log^2n)\)

P8339 [AHOI2022] 钥匙

题意:有一棵树,每个点上有一个钥匙或者一个宝箱,都有颜色,每次询问给出一条路径,求出遍历这条路径时,遇到钥匙就拿着,遇到宝箱如果有对应颜色的钥匙就打开宝箱,同时钥匙会损坏,最后打开的宝箱个数。

思路:首先,暴力可以直接用栈来维护。

考虑特殊性质 A,可以发现一对钥匙和宝箱会对所有经过它的路径做贡献,相当于是在 dfn 序上的一个矩形加,可以离线扫描线解决。

正解就是在特殊性质的基础上,考虑每一个钥匙,它能匹配的宝箱其实并不多,往每个方向至多只有一个,那么可以把这个颜色对应的虚树建出来,然后对于每个钥匙,以它问根 DFS 一遍,然后就可以求出这一对的贡献对应的矩形,最后做一个二维数点即可。

P6845 [CEOI2019] Dynamic Diameter

题意:带边权修改的树的直径。

思路:设 \(d_i\) 表示一个点到根的距离,那么答案就是 \(\max(d_u+d_v-2d_{\text{lca}(u,v)})\)

考虑在欧拉序上维护。根据欧拉序的性质,两点间的 \(lca\) 就是欧拉序上最小值,那么此时答案为 \(\max(a_l+a_r-2\min(a_l,a_r))\)

这个东西看起来就和最大子段和类似,考虑用线段树维护。每个节点维护最大最小值,答案和前后缀能对答案做的最大贡献即可。

P4747 [CERC2017] Intrinsic Interval

题意:定义一个区间是合法的当且仅当排序后是连续正整数,多次询问包含一个区间的最短合法区间是哪个区间。

思路:没发现关键性质,然后看析合树没看懂。

关键性质就是对于每个询问区间,右端点最小的合法区间就是最小区间。

证明很简单,根据值域连续段的性质,如果存在另一个区间满足右端点更大但是长度最小,那么这个两个区间的交也是合法区间,而答案就应该是这个区间交。

于是离线扫描线,然后把合法询问按左端点降序考虑能否找到满足条件的区间,可以用线段树二分来实现。

P8528 [Ynoi2003] 铃原露露

题意:给定一棵有根树,顶点编号为 \(1,2,\dots,n\),对 \(2\le i\le n\)\(f_{i}\)\(i\) 的父亲。\(a_1,\dots,a_n\)\(1,\dots,n\) 的排列。

\(m\) 次询问,每次询问给出 \(l,r\),询问有多少个二元组 \(L,R\),满足 \(l\le L\le R\le r\),且对任意 \(L\le a_x\le a_y\le R\),有 \(x,y\) 在树上的最近公共祖先 \(z\) 满足 \(L\le a_z\le R\)

思路:小清新 ynoi。

合法区间不好判断,但是我们容易判断不合法区间。具体地,假设有两个位置 \(x,y(x<y)\),记 \(z=LCA(x,y)\),那么当 \(x<y<z\) 时,会让 \(l\in[1,x],r\in[y,z)\) 的区间不合法;当 \(z<x<y\) 时,会让 \(l\in(z,x],r\in[y,n]\) 的区间不合法。

因为和 LCA 相关,考虑 dsu on tree,那么对于一个点 \(x\) 和当前枚举的 LCA \(z\),可以求出 \(a_x\) 的前驱后继,这样就可以求出和 \(a_x\) 有关的不合法区间的条件。根据 dsu 的性质,我们求出的条件只有 \(O(n\log n)\) 个,每个形如一个矩形不合法,于是离线扫描线,那么就需要维护区间加,区间历史为 0 的次数和,可以类似 Good Subsegments 用线段树维护。

复杂度 \(O(n\log^2n)\)

posted @ 2024-02-14 16:24  Xttttr  阅读(54)  评论(0编辑  收藏  举报