DS1
DS1
题单:DS1
1.成都七中
做法一:点分治
题目要求的东西与 \(x\) 联通的编号在一段区间的颜色数,暴力做是从一个点往外搜,我们想找尽量少的点向外搜索。
我们考虑一件事情,如果我们确定了连通块中的某一个点,那么其他点实际上是以这个点为中心的路径上满足要求的点的并。我们发现需要连通块的一个点和树上的路径信息,可以考虑点分治。
考虑当前分治中心 \(u\),给每个点附上一个 \(L_x\) 和 \(R_x\),表示取到这个点需要满足的最小询问区间。再当前分治中的与到 \(u\) 路径上满足询问要求的点找出来,要求即是 \(ql_i \leq L_x \leq R_x \leq qr_i\)。那么询问答案即是一个简单的二维数点,复杂度显然 \(O(n \log^2n)\)。
启示:对于连通块问题中,考虑是否能把在一个连通块中的点统一放在尽量少的位置,方便统一查找。
做法二:两棵重构树子树求交的颜色数
鸽....
2.火灾
题意:给定序列 \(a\),\(k,l,r\),求 \(\sum_{i=l}^{r} \max\limits_{j=i-k+1}^{i} a_j\)。
做法一:单调栈建树
首先考虑进行 \(k\) 次操作后序列会有什么变化,发现这个操作肯定与左边第一个值比他大的位置有关,设 \(lt_i\) 表示 \(i\) 左边第一个大于它的数的位置。
那么发现把这个东西建出一棵树来后,每次覆盖都相当于化 \(i-lt_i\) 的时间往根链上跳,最后找到最后一个满足时间限制的祖先这个点的贡献就是这个祖先的值,这样的化就可以倍增做到 \(O(nq \log n)\)。
但这样倍增的化肯定是不好维护的,这个是从下往上找祖先给这个点的贡献,这样不优。我们考虑让祖先把贡献下传,对于这棵树发现一个很好的性质。
- 祖先的值都大于子孙。
- 一个点的子孙是一段区间。
- 贡献的下传中子辈给孙辈的下传早于父辈给孙辈的下穿,那么下传时只需下传 \(a_{fa_x}-a_x\) 的值即可。
由于祖先的值都大于子孙,我们完全可以把覆盖改成加。
又因为子孙是一段区间,且每次只用下穿一个 \(delta\) 我们可以用一个四元组 \([l,r,t,v]\) 刻画这件事情,表示从 时间 \(t\) 开始,依次给 \(l \sim r\) 的值加上 \(v\)。于是我们只需要暴力判断所有事件就可以做到 \(O(nq)\),而且这个做法看起来非常有前途!!!!
我们发现这里有很多区间操作,容易发现都有可差分性,考虑把他们全部变成前后缀。那么我们每次实际上就是这样的操作,我们需要统计 \([l,t,v]\) 给 \([r,k]\) 的贡献,暴力做时统计贡献是 \(v_i \times \max(0,\min(k-r_i+1,r-l_i+1))\),把 \(\max\) 拆掉就是当 \(k \geq r_i 且 r \geq l_i\) 时 \(\min \times v_i\),发现就算要钦定大小,典型三维数点,但这个题目比较独特,我们拆贡献的时候发现满足某些条件时会天然满足某些条件,之后可以二维数点。
做法二:变为 V 个 01 序列
在下一个严格强化版本中会提到。
3.pyramid
对于这道题,发现这个操作交替进行在值多的情况下实际上是相当复杂的。由于这里的操作只有 \(ckmax\) 和 \(ckmin\) 考虑把序列转成 \(V\) 个 01 序列,即 \(a_i = \sum_{v=1}^{\infty}[a_i \geq v]\),由于判断一个位置 \(x \geq v\),只与和 \(v\) 的相对大小有关。所以可以当作 \(V\) 个 01 序列,然后我们再分析操作。
- 发现前 \(cnt\) 次 \(ckmin\),实际上是把极长 \(1\) 段的右端点往左移动 \(cnt\) 。
- 后 \(cnt\) 次 \(ckmax\),实际上是把极长 \(1\) 段的左端点往左移动 \(cnt\)。
于是发现一整次操作实际是把 \(len \gt cnt\) 的区间往左平移 \(cnt\) 个单位长度。
\(len \leq cnt\) 的区间删除。注意到所有的极长 \(1\) 区间是相互独立的,他们只会自己平移或者消失。我们可以分开考虑这些区间对于答案的贡献。
但是如果我们每次都把每个 01 序列找出来那么有 \(O(nv)\) 个区间直接爆炸。我们发现每次变化最多影响 \(cnt_v\) 个区间,而 \(\sum cnt_v =n\),于是我们发现本质不同的极长1段只有 \(O(n)\) 个,那么我们找出这些区间。
然后我们考虑这些区间对于一个答案的贡献,发现对于一个前 \(x\) 次操作,我们可以提前算出前 \(x\) 次操作需要的最小区间长度,左右端点分别的偏移量,之后就是算两个区间的交乘贡献,注意到可差分性,拆成前缀和前缀的交乘贡献,后面是朴素二维数点,复杂度 \(O(n \log n)\)。
好像还有一个用二维平面刻画 01 序列的方法,本质应该差不多,先鸽了。
4.TEST_68
做法一:分治 + tire \(O(n \log n \log v)\)
考虑维护子树补信息,先建出 \(dfn\) 序,我们发现可以类似缺一分治往下递归,不同的是这个是区间,但由于我们的区间是子树,我们知道子树是没有相交关系的。
于是我们可以把一个区间往下递归,放在区间经过中心点的地方做,然后每次扫描左指针,然后右端点同时往左缩就可以了。
做法二:找性质 \(O(n \log v)\)
我们考虑先找出两个节点,他们是全局最大异或点对。他们支配除了到两点根链上的节点,两条链可以暴力从上往下扫,每次加点就可以了。
这道题感觉挺聪明,应该是个 trick 吧,叫补信息全局支配点。
5.地牢游戏
做法:倍增分块
看到题之后第一想法是只要跳到 \(10^7\) 就赢了,那么我们如果一次加的很多是非常优的,但是我们考虑输的时候我们无法保证我们的战斗力,于是我们考虑每次尽量只做大的赢的。我们考虑根号分治,直接先跳 \(\sqrt V\) 步,然后之后我们发现 \(s_i \leq B\) 就直接赢了,否则输的我们可以钦定他输了,然后预处理出输需要的条件。这样可以做到 \(O(q \sqrt V \log V)\)。发现无法通过。
考虑倍增分块,我们发现上面我们是分成两个阶段,我们完全可以多分几个阶段达到平衡时间的目的,注意太多块空间会爆,后面平衡一下就可以了。
6.梦想歌
做法一:暴力维护,\(O(n \log n \log V )\)
https://www.luogu.com.cn/article/hdrchkoo
做法二:建kruskal重构树,找重心
这个我暂时不会。
7.旅行
题目就是给你一棵树,和一个可重的编号序列,求编号序列一段区间的虚树大小。
做法一:回滚莫队
首先我们有经典结论,对于一个点集 \(S\),虚树大小为按 \(dfn\) 序从小往大排序后,相邻两个点的距离加上首尾两个点的距离除以 \(2\) 加 \(1\) 即为虚树大小。证明考虑构建虚树的过程即可。
那我们直接用莫队做就是 \(O(n \sqrt q \log n)\)。考虑怎么优化,我们发现我们每次都是在加删点,找前驱后继。我们发现如果我们只删不加的化我们直接用一个链表维护即可。
我们考虑回滚莫队,普通的回滚莫队是做不删除的,不加的化也是类似的。我们考虑对于这个块中,先加入 \([L_{bl_x},n]\) 中的所有点。
之后如果是右端点的会那你直接向左平移的时候一个一个删除就可以了,左端点的你发现你向右移动之后,你还是要把这些点加回来,这样就又错了,我们发现我们可以倒着一个一个加回来这样就变成撤销删除了,这是可以做的复杂度就正确了,复杂度同回滚莫队 \(O(n \sqrt q)\)。
做法二:树剖+数据结构
考虑区间内的每一个点对到根链上的所有点进行染色,那么我们发现虚树大小实际上就是被染色的点的个数 $ - dep[lca]+1 $,后面一段直接减掉就行了。
前面的我们考虑给每一个点到根链的颜色记作他的编号,那么我们考虑对 \(r\) 扫描线,发现你每次把到根链上的点的颜色都染成自己肯定不劣,那我们只需要实时维护每个颜色树量就可以了,我们直接在 \(dfn\) 序上建一个 \(ODT\) 就可以直接做了,复杂度显然是均摊的,\(O(n \log^2 n )\)。
实际上不用维护 \(ODT\),注意到操作是对重链的一段前缀覆盖,于是你直接对没一条重链从浅到深维护一段颜色段即可,常数会小一些。
8.学习轨迹
先考虑暴力怎么做,发现你只要枚举第一个序列左端点,之后扫描右端点然后加点就可以了,这个做法要维护所有极大区间,非常困难。如果第二个序列存在一个必经点的话,那只需要对于每一个 \(l\) 维护一个必经点所在区间的 \(l,r\) 就可以了是容易的。
存在一个性质是,我们一定会选择其中一个序列的带权中点,证明考虑如果没有选择的话不如把一个序列选完来的优秀,后面维护随便做。
9.无人机比赛
首先考虑暴力怎么做,只需要开个 \(pq\) 记录一下每个无人机到什么地方了,还有多少个无人机,直接每次拉出队首,然后再用队内元素算贡献就可以了。
我们发现如果继续再维护 \(pq\) 的话肯定没前途。我们直接算每一步的贡献就可以了。我们发现一步的贡献只和这一步的时候有多少个无人机和对于这个无人机前面的步骤中的排名最后的一步有关,那我们只要取个前缀 \(\max\) 然后再记录后面有多少个无人机就可以了。
进一步,我们发现我们只需要每次加入一个新的无人机的时候,算一下当前无人机最后一步对之前无人机的贡献,和之前无人机最后一步对当前无人机步骤的贡献就可以了,可以 \(O(nm \log n)\)。
进一步,有 \(\sum s_i = 10^5\),那本质不同排名只有根号个,我们把这个缩起来就可以了,然后注意到里面两个操作一个是加 \(n \sqrt n\) 次,查 \(1\) 次,另一个是 加 \(n\) 次,查 \(n \sqrt n\) 次,用两个根号平衡即可,复杂度 \(O(n \sqrt n)\)。
10.水果游戏
首先这个问题静态有两个做法:
做法一: \(O(n \log n)\)。
就是你从小往大考虑值域,然后对于一个同色连续段,若是 \(len \bmod 2==0\),那么显然直接长度减半,值域加一,否则两边一定不会再有关联了。
我们直接加入
\(\underbrace{(x+1)+\cdots+(x+1)}_{\text{len/2个}}
\,x\,
\underbrace{(x+1)+\cdots+(x+1)}_{\text{len/2个}}\)
由于两边之后就无关了,所以这样做是正确的。
做法二:O(n)
考虑我们现在已经维护好了前缀的答案,考虑新加入一个元素造成贡献。设当前序列为 \(a\),长度为 \(m\),我们维护的是每个颜色的连续段。发现如果新加入的值 满足 $a_{m-1} >a_m $且 \(a_m < w\) 的话,我们需要决策 \(a_m\) 这个点,否则直接加入新的点。
我们考虑判断中间点 \(m\) 的个数是否可以整除 \(2^{\min(a_{m-1},w)-a_m}\),若可以
的话,那就把中间的点缩一下之后再合并就可以了,否则和上面做法一样,两边都加入一个新的值段,再在中间加上一个间隔符即可。
带修查区间
然后我们再来考虑这道题目,显然对于上面两种做法第二种显然是更加容易合并的,那既然这样的话我们可以在线段树上维护一段区间剩下的值,和这个区间合并出来的最大数是多少。
我们对于区间合并做上面的做法,然后对于一个段,若两段出现最大值那显然他们只能内部自己合并,算出答案并把其删去,显然每个点维护的颜色段只会有 \(O(V+\log n)\) 个,因为再多就会出现上面讨论的 “谷”,我们就会把谷继续操作成峰,于是直接放在线段树上合并复杂度就是 \(O(q \log n (\log n +V))\) 的,可以通过。
11.军事基地
做法一:ODT+线段树
每次加区间的时候直接 ODT 覆盖,每个位置维护最后一次出现值的位置。删除不好搞,直接不管了,只用线段树维护区间是否有值即可。每次 merge 的时候查一下区间又没有值再 merge,由于是最后一次覆盖的点,所以正确性显然。
做法二:线段树标记序列
还不会。
12.可爱区间
先考虑怎么判定一个区间是否合法,可以发现有下面几条。
首先最优的化,你肯定把外面的尽量小,里面的尽量大,那你把里面设成 \(B_i\),外面设成 \(B_i\),就可以了。
记新序列为 \(a\)。
- 首先 \([1,l-1]\) 的最大后缀和 $ \leq 0$。
- \([r+1,n]\) 的最大前缀和$ \leq 0 $。
- \([l,r-1]\) 的最大前缀和 \(\leq 0\)。
- \([l+1,r]\) 的最大后缀和 \(\leq 0\)。
- 若全部都放 \(A\) 时的最大子段和 \(\leq \sum_{i=l}^{r} a_i\)
考虑用一些简洁的方式来描述这些信息。
记序列 \(s\) 表示序列为 \(A\) 时的前缀和,\(lt,rt\) 分别表示序列为 \(B\) 时前缀和左边第一个比其小的,右边第一个比其大的,\(Mx\) 表示全部放 \(A\) 的最大子段和。
- \(s_{l-1}\) 为前缀最小值。
- \(s_{r+1}\) 为后缀最大值。
- \(lt_r \leq l\)。
- \(rt_{l-1} \geq r\)。
- \(sum_r-sum_{l-1} \geq Mx\)。
首先把询问拆成右端点在一段前缀且左端点在一段前缀的形式,肯定要扫描线的。
之后我们第一条和第二条可以直接标记,到位置再加上。第三条直接判。
第四条如果按套路来说应该在 \(r\) 位置加入点 \(l-1\),但是发现这样就没法维护第五条了。
但是我们可以发现满足要求的点都在单调栈上,那我们直接单调栈上二分即可,之后直接上一个历史和线段树就行了, \(O(n \log n)\)。
13 迁移计划
挺高妙的还是。
与深度相关树上操作会想到 \(bfs\) 序,但是要支持合并的化是做不到的。
可以把一个点跳到祖先看成是一个点对祖先的贡献,那只需要每个深度维护出子树贡献,并且支持合并,这个直接线段树合并维护每个深度的 \(dfn\) 序贡献即可,复杂度 \(O(n \log n)\)。
14.树上简单求和
首先数据范围明示单根号。
有了树上莫队的经验后,我们知道树上一段路径上的和是可以用欧拉序的一段区间表示出来的,第二个查询同样也是欧拉序。那我们可以预处理出第一棵树一个区间对第二棵树一个块的系数和,整块直接做掉,散块可以直接暴力找对应点,查询就直接查就可以。空间可能会爆掉,离线下来逐块做就行了,复杂度 \(O(n \sqrt n )\),常数还有点大。
15.An Array and XOR
先考虑一个莫队暴力,发现其中有一个 DP,形如
if lson && rson dp_x=max(dp_lson,dp_rson)
if lson || rson dp_x=(1ll<<bit-1) +dp_{lson/rson}
考虑一下贡献组成实际上就是一个叶子出发向上走旁边如果没有邻居就加上 \(2^{bit-1}\),求最大的叶子的权值。
那我们直接预处理出每个数在在每一位上左边和右边第一个出现邻居的位置时刻,之后限制了范围就是一个二维数点直接算一下。可能还要个根号平衡之类的东西,反正都是细节,复杂度 \(O(q \sqrt n + n m^2)\)。
16.冒泡排序机
经典技巧题。
求进行 \(x\) 次冒泡排序后,一段区间的权值和。
可以知道在经过 \(x\) 次冒泡排序后,前 \(k\) 个位置中的集合一定是前 \(k+x\) 个树中的前 \(k\) 小,证明大概是每次你会让前 \(k+1\) 个位置中最大的移出前 \(k\) 个位置,再把 \(k+1\) 的不动或是移进来,主席树维护即可。
17.序列与查询
考虑把最大子段和写成 \(k\times len +f_{len}\) 的形式,那么显然答案肯定在 \({len,f_{len}}\) 构成的凸包上面,那我们只需要求出原序列的凸包就可以了。那你经典考虑分治下去然后闵可夫斯基和,然后分治下去。当你算一个区间的时候,你先把左边的凸包建出来再把右边的建出来,最后合并一下就可以了,复杂度 \(O(n \log n)\)。
18.Equal Mex
题目显然是让你贪心划分求划分数。
做法一:根号分治
划分大小是 \(\frac {n}{mex}\),那么直接对这个东西根号分治。
- $ mex \leq B$ 弹飞绵羊板子。
- \(mex \gt B\) 只会跳 \(sqrt n\) 次考虑直接暴力维护下一次跳到哪里。对序列扫描线,值域分块,单点修 \(n\) 次,前缀查 \(n \sqrt n\)次直接平衡一下你开个 \(vector\) 存一下,下个位置再处理。
做法二:找极小mex区间
找到后,对于 \(mex\) 相同的一起处理,预处理一下倍增数组,每次直接跳就可以了。
19.平衡三元组
首先发现如果三个都可以取到最大值,直接取掉就行了。
否则你就可以固定 \(a_A = \max\) 或者 \(a_C = \max\), 然后你发现你就是在两侧的单调栈上一直往下走的过程,贡献只有两种。
令 \(i\) 是单调栈上的点。
- $\max + \max_{j=lst+1}^{i-1} a_j + a_i $。
- 注意到题目限制实际上就是两个点连线中点高于中间点,那么只需要看 \(\max\) 和考虑的点 \(i\) 和 \(lst\) 相互的斜率即可,那你发现显然操作 \(\log V\) 次之后必然满足。
于是直接暴力线段树维护区间 \(\max\) 和下标,每次暴力做就是正确的,复杂度 \(O(q \log n \log V)\)。
20.新的桥梁
首先转化一下题意,考虑如果一个点 \(u\) 是首都时对于一个点 \(v\) 的贡献。
首先以 \(v\) 为根跑出一棵树。然后你会发现一个点 \(x\) 被经过只有在 \(lca(u,x)\) 到 \(v\) 路径上最大值大于 \(x\) 到 \(lca(u,x)\) 时才有可能。画一下图就可以发现一个点 \(x\) 在 \(u\) 取 \(x\) 到 \(v\) 路径上最大边权对应点 \(A\) 的子树中的任何点时都有贡献,那么可以 \(O(nq)\) 计算。
我们把贡献的放到点 \(A\) 上计算。可以发现贡献可以在你跑最大生成树 \(kruskal\) 的过程中讨论根的方向直接算出来,之后可以建树打 tag,也可以带权并查集。
21.集宝
树上圆理论计算树上圆的交。之后要注意到的关键性质就是两个极小圆之间路径存在必经点。一个位置在一段前缀会变成极小圆,这个预处理出来。之后直接倍增跳,再把必经点之间权值算出来就可以了。
22.Tropical Season
做法一:
找出性质之后用前文梦想歌类似做法可以完成。
做法二:
倍增分块之后维护块间极差即可。
23.Induced Subgraph Queries
带权莫队板子。
要注意的是需要按 \(\sum deg_x +1\) 而不是 \(deg_x\)。
24.冒泡排序 2
首先观察到答案是 \(\max_{i=1}^{n} \sum_{j=1}^{i-1} [a_j>a_i]\)。
可能成为答案的点一定是后缀最小值。那么我们直接维护 $ f_x =\sum_{i=1}^{n} [a_i>a_x] -(n-x)$,这是一维数点直接线段树就行了,复杂度 \(O(q \log n)\)。
25.火星式选拔
首先观察一下题面,发现 \(a_i \geq b_i\) 这个性质肯定是有点用的。
再分析一下性质,考察最后剩下的集合,发现防御力前 \(k-1\) 大的人一定在里面。
考虑扫描的过程对于一个防御力在前 \(k-1\) 大的人 \(x\) 显然进入集合后就不会再出来了,如果选到 \(x\) 的时候,集合中必然存在 \(i\) 满足 \(a_x \geq b_x \geq b_i\)。那么把他踢出自己进入即可。
考虑剩下的一个人会是谁。
首先如果当前集合没有选满区间防御前 \(k\) 大的元素,那么一定会存在一个时刻选满这个区间的防御前 \(k\) 大元素,这个时间 \(t\) 显然是前 \(k\) 大防御最晚出现的一个人出现的时候,然后我们之后发现就是一个人的攻击如果大于集合中的最小防御就还掉的过程,这个直接倍增就可以。
那考虑怎么把区间前 \(k\) 大的最后一个位置找出来。这是个 \(\max\) 操作,没有可差分性我以为这个东西肯定做不了的,没想到是可以简单做的。
你发现对于第 \(r\) 棵主席树上区间的前 \(k\) 大中,如果有位置 \(\geq l\) 的数,那么前面 \(\lt l\) 的数是多少我们是不关心的,那么会被偏序掉,于是我们就可以直接维护区间最大值,再在找前 \(k\) 大的时候顺便在第 \(r\) 棵主席树上查就可以了,复杂度 \(O(q \log n)\)。

浙公网安备 33010602011771号