2025暑训日记
暑训日记
我一定要学好数据结构和字符串。
事实上已经进行了一周左右的训练了才开始写这个,因为突然觉得博客的记录意义更大。
cf已经荒废了很久了,其实也知道光会数据结构没啥用,不过我觉得还是 bfs 再说,好歹技能树得点个大概。
2025.7.28
事后补记一下,这段时间大概在进行线段树的内容,包括线段树对复杂信息的维护,从代数结构的角度来理解线段树,线段树套矩阵等方法,从 ALex_Wei 的博客里面学习到了很多很好的代码实现方法。
简单学了一下线段树二分,原来就知道个大概,主要多学了个对于局部区间 \([l,r]\) 的二分方法(这个没有上手写,感觉写不太好)。
2025.7.31
总结一些最近几天的数据结构学习和锻炼
今天晚上脑子真的宕机了。。。对着一道例题debug了三四个小时,最后发现各种小问题,包括不限于 "+=" 写成 "=" 、"r<=qr" 写成 "qr<=r" 等细节错误。。。接口是数据结构就是这么恶心,事实是自己还是火候未到。
平衡树
其实进行了简单的线段树学习之后就开始平衡树学习了。
Splay
最先学的是splay,核心在于通过“单旋”和“双旋”达到“伸展”的目的,使得每一次操作后的节点能够变成根,复杂度证明是均摊的,不会,这个由于大概只有在lct中才会配套使用,所以就干脆不学这个平衡树了,以后有需要再来补足。
FHQ_Treap
目前为止见过最精妙的数据结构之一,尽管常数稍微大了一点,但是功能强劲,最重要的两个方法就是 split 和 merge,让 FHQ 能够灵活的基于值域或者下标对于整棵树进行操作,每个节点随机权值,以达到期望单次操作复杂度 \(O(n\log n)\) 的目的。
可以写文艺平衡树,一开始没有太理解tag的原理,后来发现,实际上是每个点的 下标 构成了一棵 BST,而非其权值,所以我们按照 size 而非 val 来 split,这样分割出来的 BST 就是按照下标排列的。
对于 tag 的理解,也是思考了很久,个人认为关键就在于理解 split/merge 之后,树的中序遍历序列不改变,所以如果在操作前下放 tag ,再把相应的树裂出来,会发现所有的值都是正确的。
后来思考了FHQ能不能对于 值在 \([l,r]\) 内的所有 \(a_i\) 进行区间加,发现只有当加的数 \(\le 1\) 的时候才能正常做,究其原因,大概是因为merge只能把 一个小的Treap拼到另一个大的上面去。
主席树
就写到了可持久化单点修改数组,思维和实现难度都不高。
接着学习可持久化区间修改区间查询数组,这个题就困扰了我一个晚上,并不是因为题目本身有多难,而是我后知后觉地发现:自己在树状数组维护二位前缀和这一块根本就没有深入理解。
对于 \([l,r]\) 的操作,要分成两个独立的部分看待,每单点修改一次差分数组,考虑对前缀和数组的贡献,然后拆成两部分维护。
这下应该不会忘记了。
后面就出现了上述的一些傻逼错误。
回到寝室之后又用标记永久化的方法写了一遍,精髓在于每个节点并不储存其精确代表的值,而是只储存以其为根的子树内的精确值,在查询的时候统计从根到当前节点的所有 tag,最后一并加入答案。
这样做的好处在于,每一次修改原线段树至多新建 \((2\log n)\) 个节点。
大概就是这样,数据结构这一块,明天打算把之前的内容做一些习题。
锻炼
坚持锻炼三天了,虽然今天晚上实在过于晚了,就跑了 \(8\) 圈。
2025.8.05
最近仍然是在数据结构这一块进行训练,但是由于摸鱼程度太高了,加上做的例题都是稍微超出自己的能力范围的题目,所以推进进度实在过慢了,截至目前大概把除了可持久化平衡树之外的其他基本可持久化模板题加上基础应用给过了一遍。
下面写一些流水账:
静态区间kth(可持久化线段树2)
尝试用平衡树过可持久化线段树2,也就是静态区间kth,写完发现按照siz分裂的情况下成功白忙活地用log的加大常数的复杂度做到了数组寻址O1就能做到的事。de了半天bug发现是线段树递归的时候没有把子节点传到参数里面 (什么是“可减信息”?理解了之后方能够大概体会可持久化线段树解决这类问题的原理),每个时间点的线段树都存的是“前缀的信息”。
可持久化01Trie
然后做异或粽子翻了翻之前在 CF 上面写过的 01Trie 题,发现自己只会 \(O(n^2klogV)\) 的做法,倒闭完了。后面发现这个做法也是假的,原来是题读错了,现在只会0分做法了。之后发现把题读难了,知道了正确题意之后会了 \(O(n^2)\) 做法,不过这题正解需要两个 Trick :
- “给定 \(v\),从 \([l,r]\) 中选取一个数,使得 \(a_i \oplus v\)”的值最大,为了掌握这个Trick,去做了一道紫的模板,数据结构的代码主要注重逻辑和细节,可惜我没有。
- 从 \(n^2 对里面选 k个最大/最小 的应该怎么办\),两种方法,当 \(k\) 较小的时候,可以把选取状态放进一个大根堆里面,然后贪心地选取 \(k\) 次;当 \(k\) 较大的时候,二分这个最大/最小值的界限,然后根据这个界限进行相应的答案统计。例题是 P1631序列合并 和 P2048 超级钢琴。这俩例题都属于是知道了思想之后就一遍过了,虽然说还是花了不少时间,但至少没有经过痛苦的 debug。
属于是遇到不会的知识点就去开几道例题,这样虽然学得慢,但是满足了 bfs 的条件,但愿多多益善了。
做了例题之后就写了这道题,debug 的过程也是十分的折磨,忘了是因为什么疯狂 wa 了,大概就是脑子不太清晰,导致细节处逻辑错误。
然后就写了这道题的加强版 CF214B Friends,也就是 \(k\) 的值达到必须用二分的地步。不过 01Trie 的结构是二分友好的,这提供了一个解决思路。并且可以通过 \(k:=2\times k\) 来把这个 \(i<j\) 的条件消除掉,最后计算出来的答案除以 \(2\) 即可,因为初始深度开小了导致 wa 了无数次,但是奇怪的地方在于明明估算出来的范围是完全足够的,但是就得多开一点才可以通过。
除此之外,还积累到了 \(MOD^{-1}=\frac{MOD+1}{2} \mod MOD\) 的小知识。
可持久化并查集
写之前觉得就是在并查集下面把普通数组换成可持久化数组的接口就行,但是好像没有想的那么简单,之后发现自己对并查集的掌握完全都是不牢靠的。
于是重温了一下 路径压缩 和 启发式合并 这两个知识点,自己尝试证明了一下复杂度,不过路径压缩的证明不会,两种方法拼到一起的 反阿克曼函数 复杂度也不会证明。
例题可以写路径压缩的版本,但是修改实在太多了,导致空间不足够,所以最多只能做到 \(92pts\) ,写了按照 siz 合并的版本,改了很多次终于通过了。
BTW: 想要完全实现等同于普通数组的接口还是有点困难,其中赋值操作好像涉及到了 Proxy 代理之类的知识,让人一头雾水,并且好像也不是很能很好地把 “时间戳” 这个概念藏在接口里面。
过题统计
可能不完全,毕竟没有在写完的当天记录。
- P1631 序列合并 绿
- SP11470 TTM - To the moon 紫
- P3834 【模板】可持久化线段树 2 紫
- P4735 最大异或和 紫
- P5283 [十二省联考 2019] 异或粽子 紫
- CF214B Friends 紫 2600
- P2048 NOI2010 超级钢琴 紫
- P3367 【模板】并查集 黄
- P3402 【模板】可持久化并查集 紫
- ABC372E K-th Largest Connected Components 绿
2025.8.7
可持久化线段树
做了 P2633 Count on a tree 这一主席树在树上路径查找 kth 的问题,利用 dfn 拍平后会发现这些区间是零散的,但是至多有 \(\log n\) 个,因此不难想到去二分这个 \(k\) 小值,然后暴力地在这些区间里面查有多少个数 \(\le kth\),总的复杂度是 \(O(m\log ^3n)\) 的,但是我也是不太清楚能不能通过 \(10^5\) 的数据,只能先写一些然后顺便注意一下常数,没想到直接过了。
参考题解之后,发现其实不用树剖,主席树的关键性质在于其“前缀性”,应用到树上,刚好有与之契合的“树上前缀和”,所以在树上 bfs 建立主席树之后,类比着静态区间 kth 来做就行。注意点是传参的时候应传 \(root\),并且 \(x,y,lca(x,y),fa[lca(x,y)]\) 这四个参数都应该传进去。
然后又做了一道例题,本来很简单,但是硬是改了两个小时,原因在于把差分数组当成原数组了,纯唐诗来的,改了还是错,遭了半个小时发现是为了在线用来存答案的变量没开long long,我太依赖define int long long了。
然后做了 线段树 和 等差数列 相关的题目,大概就是类似查询 \([l,r]\) 之内的数排序之后是否能构成一个等差数列(或者变式问题,如 CF526F ),主要就在于三个条件:
- \(Max-Min=(r-l)\times d\)
- \(GCD(a[l+1]-a[l],a[l+2]-a[l+1],...,a[r]-a[r-1])=d\) ,这个条件也可以换成区间内所有数模 \(d\) 同余。
- 区间内的数不重复 (对于 \(d\ne 0\) 的情况而言)
然后做了一道紫题,是推广到公差为 \(k\) 的情况,恶心坏了,de 了半天没改出来,真的也不知道错哪里了,不想继续做了。
然后继续去做 CF407E,希望不会卡题到绝望,虽然下午有学校的个人赛,会占用一部分时间就是了。
总的来说,这类题目有两种类型:
- 统计等差数列区间数量。
- 判断一个区间是不是 \(k\) 等差数列。
就在今天!终于把这道拖延了两天的题目给过掉了,还是一次提交就通过了这道3100,也是有点不敢相信,不过实际应该就是2500左右吧,知识点涵盖了线段树二分、单调栈、线段树维护区间最值、以及等差数列的转化。把等差数列转化后用单调栈维护的前置知识和题目过掉之后,发现以自己对“线段树二分”的理解,还不足以解决这道题“在 \([l,r]\) 内找一个最小的 \(i\) 满足 \(a_i\le v\)”的问题,因为我始终还是觉得“二分”这个东西多少得是具有单调性的,浅浅学了一下之后发现“线段树二分”其实是利用线段树的分治结构来做一些事情,可能也不局限于“二分”本身,反正我觉得偏向于“分治”一点。
今晚回寝室之后再做几道线段树二分的题目,把线段树二分的初步学习笔记给写了,明天就开始学习线段树合并和线段树分治这两个知识点了,这俩学完了之后,就差一个树套树就能把 提高级数据结构 这一块基本过一遍了,应该这周之内能做完,按照暑训的计划,接下来就是去学学字符串的内容了。
比较悲哀的事情是现在的大部分题目都是偏向思维的了,学了这一身本领,要是到时候只能在场上当战犯,那就太不尽人意了。想来还是要在 abc 和 cf 上面同步推进才是。根本原因还是阅历太少导致的,感觉自己就像是在越级打怪,总感觉基础和技能树之间差了一些需要时间去沉淀的东西。
过题统计
- P2633 Count on a tree 紫
- P3168 [CQOI2015] 任务查询系统 蓝
- CF526F 紫 3000(虚高)
- P3792 蓝 (连续段/等差数列的等价转化,用 RMQ 解决问题)
- CF407E k-d-sequence 紫 3100(可能虚高)
- Leetcode 3479. 水果成篮III 困难 (线段树二分板子)
2025.8.8
可持久化线段树与MEX问题
看这样一个经典的问题:给定序列 \(A\),和若干次询问 \([l,r]\),每次要求求出 \({a_l,a_{l+1},a_{l+2},...,a_r}\) 中的 MEX。
例题P4137
情况1
如果说序列 \(A\) 自己是一个 permutation 或者 内部元素不重复,那么直接对下标扫描线,并且建立一个可持久化权值线段树,这样就容易通过在权值线段树上二分来找到第一个为 \(0\) 的位置,判断答案是否在一个线段树节点内的方式是去检查是否 val[x]==r-l+1,然后就按照线段树二分的方式递归查找即可。
情况2
如果不保证元素不重复,那么这种做法就失去了正确性,因为我们可以默认 “\(val[x]==r-l+1\)” 与 "区间全满" 等价的前提就是元素互不相同,否则就可能造成区间不满,但是仍然有 \(val[x]==r-l+1\) 成立的情况出现。
这时应该怎么做呢?回顾在 区间静态数颜色 的经典问题 HH的项链 中我们用到的 trick,对于右端点扫描线,然后把所有出现过的数尽可能地“挂”在最右边,举个例子,如果 \(v\) 目前出现在了 \({1,3,4,5}\) 这些位置上面,那么我们记录 \(pos_v\) 为 \(5\),这样能保证当给出一个以 \(l\) 为左端点的查询时,不会漏掉任何一个答案,也不会算重(因为每个数只计算了一次)。
这个开了个专题写在博客里面了,日记里面就不再占用篇幅。
树的重心和树剖
接着去做了做学校的题单,水题就不写在过题记录里面了。
主要强化了关于“树的重心”和“树的直径”的相关概念,这部分主要是之前自己高中时期囫囵吞枣学习的补足,了解到了树的重心可以通过求出根节点的答案之后向子节点递推,同时对重心的判定不一定就依赖于那个“距离求和式”,可以通过定义判断,去掉这个点之后剩下子树的最大 \(size\),在我们尝试找到重心时,判断这个最大 \(size\) 有没有减少就好,因为重心处的该值一定是最小的,并且在这个地方取到离散的极值,往两边走会呈现出类似二次函数的增长。
关于路径上的最小路径最大,其实可以转化成最大生成树问题,因为题单里面有 货车运输 这道题,所以顺带提一嘴。
其他题目
做了 P11210 『STA - R8』强制在线动态二维数点 这道题目,其实和二维数点没有什么关系,一开始想着旋转坐标系之后去解决这个问题,结果发现询问的区间变得不规则了,遂作罢。看了题解之后才发现这题非常需要一点观察力才能够转化到线段树二分上面去,如果能够找到一个合法的答案,那么之后就算统计到了不符合条件的答案,也不会比这个已经找到的合法解更加优秀,所以直接大胆 RMQ 就行了。
P3293 [SCOI2016] 美味 乍一看是个 01Trie,结果发现还加了个常数,束手无策了,但还是从高往低贪心,然后、、就不会了,看了看题解思路差不多,直接这样做,相当于利用线段树的二进制结构在值域上面倍增,所以一定能够达到最后的最优答案,前提是值域比较小。
这也启示我,可持久化线段树在解决最大化异或值的问题下,如果至于范围很小,可以代替可持久化01Trie。
过题统计
- P4137 Rmq Problem / mex 蓝
- HH的项链 蓝
- CF817F 2300 蓝
- AT_abc292_h 蓝
- P10391 [蓝桥杯 2024 省 A] 零食采购 绿
- P4427 [BJOI2018] 求和 绿
- CF685B Kay and Snowflake 2000 绿
- P11210 『STA - R8』强制在线动态二维数点 紫
- P3293 [SCOI2016] 美味 紫
2025.8.12
ABC
弄死我也想不到最后是因为枚举的 \(\frac{1}{2}\) 常数导致的 'TLE',太幽默了这个傻逼E题。
珂朵莉树
其实我也不知道这个东西和“树”有什么关系,甚至我觉得和数据结构关系也不到,主要的思想就是通过 set 来维护一个基于值域或者下标的颜色段。其复杂度的证明比较复杂,当数据随机的时候可以做到近似线性,这也意味着可以被卡。。。总之其效率十分依赖于区间推平的 assign 函数,属于是不那么常用的数据结构。
代码并不难想,主要是分裂的一些先后顺序和细节。写完了之后做了两道例题练手。
线段树合并
线段树合并是基于动态开点线段树的一种信息合并方式,作用是把两棵范围相同的线段树中的信息集中到一起,顾名思义很简单,只是实现起来会比较难受。可以类比着 FHQ 的 merge 来写,merge 函数返回值是合并后的编号,用递归的方式来完成树的合并,一旦遇到当前 \(x\) 或者 \(y\) 为空就直接返回非空的节点,或者遇到叶子结点,这时的合并是容易的,所以合并之后直接返回新叶子结点的编号。
注意到每次合并的操作次数实际是两颗线段树重合的节点数,所以如果要进行 \(n\) 次合并,那么最后的总复杂度就是 \(\sum_{i=1}^n 节点数_i\),也就是总结点数,常见情况是合并 \(n\) 棵都分别只有一个点有值的线段树,时空复杂度都是 \(n\log V\)。
这里的合并具体来说可以用可持久化的方式实现,也可以通过直接覆盖的方式实现,取决于空间限制或者是否需要对历史节点进行访问。
又做了几道线段树合并的习题,简单梳理一下心得:初步体会来说,线段树合并是一种结合集合的合并(可联系并查集使用)来同时合并 大规模可交换 信息的一种线段树应用,可以解决子树问题和一些图论联通块问题;另外,在序列染色问题上也有应用,对于某个位置,肯定只有一种颜色,我们不必要也没办法对其使用线段树进行维护,不妨换个角度思考,对于颜色维护其存在位置,而不是对于位置维护其上的颜色,这样我们在修改颜色的时候,就可以通过线段树合并的方法快速合并两个集合的信息。
反思在求解 静态区间MEX 问题的时候,我们也是运用了一致的思想:对于每个数维护其下标,而非对于每个下标维护对应的数字,结合可持久化线段树二分,就可以 \(\log n\) 求出区间的 MEX。这启示我们,在陷入长考的时候,可以试着转换一下变元的主次关系,再试着解决问题。
线段树分裂
和 FHQ 的分裂十分类似,唯一不同的地方在于,叶子结点有时不能直接 return,需要结合情况对叶子结点的分裂进行分类讨论。还没做例题,明天做。
过题统计
- CF896C 2600 紫
- CF817F 2300 蓝
- P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并 紫
- P3224 [HNOI2012] 永无乡 紫
- P3201 [HNOI2009] 梦幻布丁 紫
2025.8.15
最近的体会:数据结构不会考查裸题,一定是在某种柳暗花明的转化之后才能进行应用,因此进行思维和 trick 的训练是十分重要的!!!
类扫描线
CF1996G,还是转化失败了,自己永远功败垂成,想不到关键的那一步。通过转化可以实现成类似于扫描线的区间加减,查询全局非零个数的问题,这样的话就可以通过标记永久化解决。
线段树合并
值域线段树合并可以在 \(O(\log n)\) 时间内做到合并两棵线段树,如果这两棵线段树是两个序列分别的桶排的话,我们就在 \(\log n\) 的时间内做到了合并两个有序序列(甚至可以把正序和倒序的序列合并在一起,只要记录一下正序逆序的 \(tag\) 就行了)。
例题 P2824 [HEOI2016/TJOI2016] 排序、CF558E A Simple Task。
这种方法的局限性在于只能单点查询,如果说想要知道排序之后的整个序列,需要用到另外的方法。
CF558E A Simple Task就是一个弱化版本,因为字符集的大小只有 \(26\) ,所以直接开 \(26\) 个桶来维护区间推平和计数,每次操作都去暴力遍历 \(26\) 棵线段树即可。
一旦字符数变多,例如扩展到整数范围内,显然沿用上述方法会导致时空复杂度两方面都出现问题,于是考虑另外的方法。用 set 维护极长有序段,对每个极长有序段开一棵权值线段树,这样我们就可以以 \(O(n)\) 的代价知道当前段内的结果,并且合并两个有序段就是在进行权值线段树的合并,代价是 \(O(\log n)\) 的。于是类似于 ODT 的操作,我们先在 set 中二分,并同时线段树分裂出对应的部分,然后进行若干次线段树合并,就能够维护好这样的有序序列。
接着完成了一道查询树上所有 \(p\) 级儿子的个数的题目,std 给的做法是树上启发式合并,不过我觉得线段树合并更好写,就直接日了个数据结构上去,感觉代码能力在逐渐变强,或者说对这一块至少写的越来越得心应手了,最近基本都没有出现红温 debug 的情况,这道题更是一遍就过了。
然后写了这道题的加强版,简而言之就是不仅需要维护对应深度的数量,而是需要对于每个深度维护一个不可重集合,那就很自然的能够想到使用 set 了,对于每个线段树的叶子结点开一个 set 来记录所有的不可重元素,在合并叶子结点的时候要记得 启发式合并。
花了一整天去思考CF490F Treeland Tour这道题目,但是还是没有想好树上启发式合并怎么写,于是只好去学习线段树合并的做法,这也是十分牛鼻的一个思想,在目前做过的所有 线段树合并 题目中,这是我认为最为精髓的一道题,于是写了个博客。
最体现分治思想:CF490F Treeland Tour
最体现维护DP:P4577 [FJOI2018] 领导集团问题
ABC补题
做了一场 ABC395,基本上五十分钟内把 A 到 F 做完了,E 是个分层图最短路板子,F 还是思考了一会,想着这种约束比较多的题目一般是朝着化归到简单问题的方向上发展,然后就意识到答案是具有单调性的,最坏的情况是把牙齿全部砍掉,但是一定存在一个答案的分界点,使得这个最后的答案在这个点是可行的,但是再多一点就不可行了,于是 check 便转化成了维护 \(n\) 个区间的交集,顺便判断是否为空集即可。
感觉经过了一定量的训练之后,至少绿题多多少少都有些想法或者能够自己解决了。
树上启发式合并
我感觉和 dsu 的关系不大,但它就叫做 dsu on tree,我也没有什么办法。
核心思想在于启发式合并,如果在合并两个集合的过程中,我们始终保持把小的集合合并到大的上面去,那么对于小的那个集合而言,其 \(size\) 就成功翻倍了,也就意味着,在合并到只剩下一个集合之前,最多进行 \(\log n\) 次这样的合并。同时,即使说每次合并,我们都暴力地遍历小集合里面的每个元素,每个元素到最后也最多被统计 \(\log n\) 次,总的复杂度是 \(O(n\log n)\)。
树上启发式合并 主要被用来解决一些 可离线子树查询 的问题,对于一个以 \(x\) 为根节点的子树,算法流程如下:
- 遍历所有 \(x\) 的轻儿子,计算其答案,但是不保留其对于 用于记录信息的全局数组 的影响。
- 处理 \(x\) 的重儿子,计算其答案,并保留其影响。
- 暴力遍历所有轻儿子及其子树内的节点,逐一计算每个节点对于 \(x\) 子树的答案影响,最后还要注意计算 \(x\) 节点本身。
- 如果 \(x\) 是被作为轻儿子遍历,那么消除 \(x\) 内所有节点的影响。
其中删除影响和计算答案都是暴力遍历所有子树内的节点,可能会对其复杂度产生疑惑,但实际上这是对的,因为只有在当前树是轻儿子的时候才会触发暴力遍历,每次遍历之后,轻儿子都被合并到了重儿子上面去考虑,也就是说轻儿子所在集合的 \(size\)翻倍了,这就是启发式合并的关键所在
完成了一道练习题,大概就是给定一棵有点权的树,可以把任意点的点权修改为任意整数,找出最小的修改次数,使得整棵树不存在异或和为 \(0\) 的路径。昨天晚上没写,睡觉的时候大概想了一下,发现首先树上路径问题可以转化成到根节点的前缀和,这样就变成了一个相对来说比较容易维护的量,其次如果把一个节点改为一个极大值,那么任何经过这个节点的路径都不可能满足异或和为 \(0\),所以这样贪心应该是没有什么问题的,具体的证明应该结合递归来讲,在当前点 \(x\) 的 dfs 的目标是去统计子树内经过 \(x\) 的需要修改的路径条数,也就是说,所有 \(x\) 的子树内需要考虑的需修改路径,我们已经通过递归考虑过了,那么在当前根节点进行修改自然成了一个不劣的选择。
这样的话,其实树上启发式合并除了解决常规的离线子树查询问题,还可以解决线段树合并相对来说不那么好解决的 路径问题,不过下这个结论还是过早了,再多做几道题来体会一下。
分别用 线段树合并 和 树上启发式合并 把 CF1009F Dominant Indices 做了一遍,给我的感觉是线段树合并很好写,并且在处理 树上深度相关问题 上面有得天独厚的优势,但是一旦遇到空间要求较高的情况就比较难受。树上启发式合并也是类似于 数位DP 的套板子算法,但是可以做到空间线性,或许常数会小一些。
然后又做了几道树上启发式合并的习题,包括一道区域赛曾经出现过的题目,这里就不展开来讲了。
过题统计
- P5494 【模板】线段树分裂 紫
- CF1996G 2300 蓝
- CF558E A Simple Task 蓝 2400
- P2824 [HEOI2016/TJOI2016] 排序 蓝
- CF600E Lomsat gelral 紫 2300 线段树合并/dsu on tree 板子
- CF208E Blood Cousins 蓝 2300
- CF246E Blood Cousins Return 紫 2600
- CF1709E XOR Tree 蓝 2400
- CF1009F Dominant Indices 紫 2300 (线段树合并+树上启发式合并)
- CF490F Treeland Tour 紫(实际蓝 2200,但是写的紫做法)
- CF570D Tree Requests 蓝 2200
- 2020长春F
- P5459 [BJOI2016] 回转寿司 绿
- P4577 [FJOI2018] 领导集团问题 紫
2025.8.18
周二之前一定学完树套树,然后开始字符串。
线段树
写了一道类似于线段树维护动态单调栈的题目,实际上也不是单调栈,这道题的精髓主要在于让我认识到了:线段树的 pushup 不一定是 \(O(1)\) 的,在某些特定的情景下,可以在 pushup 的时候强制递归 \(\log n\) 层到叶子节点获取必要的信息,然后在计算当前层的值。
树套树
总的来说就是有些带修问题不能用单一的数据结构解决,比如既要对于下标进行修改,又要在区间内进行平衡树的 kth 和 查询 rank 操作,这时候一般采用树套树来解决,目前见过以下几种:
- 线段树套平衡树,线段树维护下标,平衡树维护线段树节点对于序列区间的集合,查询 kth 的时候要转化成二分 kth 的值,然后查询 rank。
- 树状数组套权值线段树,树状数组本质上是基于二进制对序列下标的分块,所以外层树状数组维护下标,内层维护权值即可。
- 如果先对值域进行二分,然后再在外层树状数组上面找 \(log\) 个节点,最后再在每个节点上面进行 \(\log n\) 的线段树查询,那么总复杂度会是三个 \(log\)。
- 实际上可以先将这些 \(log\) 个节点收集起来放到一个集合里面,然后对值域 \([L,R]\) 进行考虑,同步计算这 \(log\) 个节点的左子节点在 \([L,mid]\) 中有多少个数字,分别加减,然后就可以和普通值域线段树一样二分了。
想着没有个自己的板子不太行,于是一直在自己闭门造车树套树的板子题,然后就硬干了两个小时,最后终于调过了样例,真的是一把屎一把尿地把这个丑的批爆的程序调出来了,发现最后还误判了空间复杂度,实际上树套树的空间复杂度应该是 \(O(n\log^2n)\) 级别的。不过对最终版本仍然不太满意,重载了三个版本的 query ,差点连自己都没分辨的过来哪个是哪种功能,好在是有自己的板子了,以后有时间或许能重构一下(然而发现自己权值线段树实现平衡树的板子甚至没写)。
个人赛
最搞笑的一集。
本来前期开题太慢,觉得自己这次可能是完蛋了,第三题就是一个感觉不太可做的题目,八个方向随机移动求字符串最大值,这种显然无法 dp,有没有什么好的贪心写法,好像也没有,感觉这题也没有那么简单啊,复制到洛谷看了看翻译,自己并没有读错题,结果就是个橙题,彻底整不会了。十多分钟写了个最优性剪枝的爆搜上去,结果果然第二个样例就TLE了,彻底完蛋,再重读一遍题目,发现选定的方向是nm不可更改的,这些在原来爆搜的框架下修改一下就可以通过了。
赛时感觉到有很多不可做题,估计是 AGC 和 ABC 拼到一起的场次,只能开了个看起来还能做的邻接矩阵题目,显然只要求出 \(A^2\) 就可以了,不过不幸的是,我只会 \(O(n^3)\) 的矩阵乘法,于是开始向图论方向考虑,发现无论如何修改做法,其本质都是矩阵乘法,有点绝望。于是竭尽全力想着去优化一下矩阵乘法,由于邻接矩阵的特殊性,这里的最内层循环实质上是在求两个集合的交集,很容易想到 bitset 搞一下,但是 \(O(\frac{n^3}{\omega})\) 真的能过吗?只能死马当活马医了,图中发现自己不会用 bitset 了,还去求助了 deepseek,爆笑。结果交上去就过了。。。(赛后查看题解甚至发现正解就是 bitset )。
A 是个显然用倍增预处理一下就可以通过的题目,然而倍增数组开小了 wa 了两发。
本来都不想做了,剩下的题目应该都不太可做,开始玩起了手机。不过感觉这样不太好,还是象征性地开了一道题,结果发现这道题的贪心异常明显,只需要把一个点到四个 \(\text{Main street}\) 的状态存起来,然后 \(4\times 4 +1\) 做一个枚举就可以了,每次计算都是 \(O(n)\) 的,还寻思这么简单的题目怎么没人开呢。。。结果到最后连样例都无法通过。
后来发现有一些 corner case,会导致实际最优解并不是曼哈顿距离,而是需要小小地绕路。改了以后仍然是 wa,不想做了。第二天发现原来是判断这种情况的 if 条件也写错了,改了以后就过了,说实话细节确实有点难受。
CDQ
然后是做了一道三维偏序,本来是想着用树套树来写的,但发现 \(n\le 2\times 10^6\) 好像十分不支持 \(n\log^2n\) 的空间,于是只能用 cdq 分治来写。
首先是十分常见的把平面内的矩形询问拆成四个小询问的简单容斥。除此之外,也是对自己之前对 cdq 算法的误区进行了更正,计算贡献和消除贡献的时候都只用考虑左区间,因为我们是基于第一维偏序考虑,统计对于每一个右区间的元素,左区间内的贡献是多少。
至此,又拖沓了差不多一天,终于开始字符串的学习了,大部分板子题目大概还是记得比较清楚,但是 exkmp 以及 ac自动机 的部分大概是有所遗忘了,需要重新训练一下。
Manacher
做了一道马拉车的变式P3501 [POI 2010] ANT-Antisymmetry,大概就是把判断条件中的 == 符号重新定义了一下,但是有个坑在于其中奇数长度的串是没有意义的,所以我们并不用去讨论非特殊字符的位置,否则就会出现 用不合法的中心去更新 \(maxr\) 的情况 ,导致后续计算出现错误。
实际上,我们在 manacher 递推的时候不必要每个位置都遍历,只需要去遍历我们想要计算出 \(r[i]\) 的位置即可,因为这是均摊的复杂度,本质是暴力。
过题统计
- P4198 楼房重建 紫
- P3380 【模板】树套树 紫
- P3369 【模板】普通平衡树 蓝
- P3157 [CQOI2011] 动态逆序对 紫
- P4390 [BalkanOI 2007] Mokia 摩基亚 紫
- UVA11475 Extend to Palindrome 绿
- P3501 [POI 2010] ANT-Antisymmetry 蓝
2025.8.22
Manacher
做了一道习题,在统计马拉车回文串相关答案的时候一定要记得去结合实际串,比如 特殊填充字符 本身实际意义下是一个空串,这一点要尤为注意。
ABC训练
开了一场ABC,被一道树形dp卡了很久,但是实际上并不用考虑如何设计复杂度状态,从叶子结点开始考虑,这样就可以直接确定一些状态,接着根据当前节点 \(x\) 的儿子数量进行考虑,这样就使得状态减少为两种,大大减小了父级节点转移的自由度,这也体现了“复杂问题思考简单情形”的优势。
接着就是 F 题,采用主席树进行暴力 \(\log n\) 计算区间颜色数量是行不通的,因为无法避开枚举区间所带来的 \(O(n^2)\) 复杂度,那么肯定就不能枚举完这个区间,我们考虑分治一下这个问题,当一个分界点固定的时候,问题转化成再找一个分界点,使得这个位置的 \(pre_i+suf_{i+1}\) 最大,看起来好像能够 rmq 的样子,那么在端点移动加入元素的时候如何对这个权值进行修改呢?答案是可以的,不难想到记录一下每个数最近出现的位置,当前这个数的出现对于所有 \(pre\) 的值没有影响,那么就会使得所有 \([pos[a[i]]+1,i]\) 的 \(suf\) 加上 \(1\),这样就可以写区间修改,并且查询最值的线段树了。
组队赛8.26
这场总体来说其实什么也没干,不可替代值为-INF,写了两道签到题,一道 dijkstra 板子,一道 ac自动机强化版 板子(甚至这个还是现学的)。
全程被学弟带飞了,然而人家还说“今天状态不好,拖了我们后腿”,其实真正忐忑的是我,要是他知道了我的实际水平和他心中的期望实则大相径庭,会是何等的失落呢?不过踌躇不前不能解决问题,既然意识到了残酷的现状,就要想办法去改变,“菜就多练”是补足自己的唯一办法。
接下来一直到假期结束的一段时间之后,我个人的安排大概都会是专题板块+abc训练,偶尔会加入一些cf。
专题部分打算把字符串的ACAM、PAM、SAM初步学习完了之后,就暂时放一放更深度的学习。然后去把计算几何初步给学习一下,至少说要有自己的板子。
AC_automaton
既然学了新知识,那就学到底,回到寝室之后重新仔细阅读了关于 AC自动机 的博客,可谓是常看常新的。
然后写了一篇初步的学习笔记,丢在博客里面了。
个人赛8.27
最近的生物钟似乎是不太理想。。。。早上睡到十一点,一点就开始组队赛,晚上或者是找个地方做点 ABC 的题目,或者索性一摆到底。以至于现在字符串的学习进度十分缓慢,就这样吧,反正明天就回家了。
今天的训练赛算是没有十分唐的操作,一道背包题很快地意识到了交换答案和状态的定义,然后就能正常枚举转移了。后面一道博弈的题目卡了很久,后来发现原来一个博弈的细节想错了,实际上答案应该能够更大,改了两次之后才通过。然后用 ODT 过了一道区间推平加染色的题目。
最后的一道类似数学题的题目并没有做出来,于是最后一个小时开始摆烂,看起了川端康成的《雪国》,故事挺有趣的,就是和日本文化联系有点多,有时候可能会不太能读懂。
过题统计
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/19014392

浙公网安备 33010602011771号