2022 9 月总结
九月份/fendou
这个月份主要导向 whk,与 OI 拉开一定距离之后,看得更清楚了。
(话说我这样发总结是不是把 FZOI 卖了/jk/jk/jk)
9.1
发现 8 月结束了,然后把 8 月总结发了出去,从头到尾大致扫了一遍,发现有一道构造题当时是半懂不懂,想了一想发现自己的想法的问题,然后把视频找出来重新学了一下。
在做 [UNR#5] 诡异操作。
wjl 来问 区间和,然后我推翻了昨天我的单 log 结论,觉得 log^2 很对,因为达到阈值之后需要递归下去算一遍新贡献,对吉司机线段树的复杂度是有影响的
然后又分析了一下,又觉得单 log 是正确的。
影响在于 \(mn < v < sub, v > val\) 的时候,需要往下面递归,此时对吉司机的势能不会改变,但是发生了有复杂度的递归操作。
如果简单地认为,每一个点只会发生一次跳跃闸值的动作,为了找到这些位置需要 log 的额外时间,那么就是 log^2 便错了。考虑从一个点往下递归之后,会再次往下面递归当且仅当这个节点也发生跳跃闸值操作。
所以复杂度就是 吉司机的 nlogn + 跳 4n 次闸值所产生的递归,大常熟而已。
哈哈哈,被推翻了一小部分,原因是没有从头到尾都没有把我的代码完整地看过一遍。
上述所 yy 的:每次因为跳闸值而产生递归,能够继续递归的条件是这个节点也需要跳闸值。
但是问题是:存在当前节点不跳闸值,但是最大前缀边长的情况,即 v < val 且 v < sub 的情况下,更新了最小值使得当前节点前缀变长但是没有改变更新方式。
所以需要 val = min(val(ls), val(rs)),即如果子树内存在增长,这个节点也要增长。
即,每一个节点只会改变一次更新方式,但是可能会跳多次闸值。
对于一个会改变更新方式的点而言,会有 log 个祖先因此而受到波及(因此而产生 log 次不会使得势能减小的递归),所以复杂度还是 nlogn,只是常数更大了/jk、
c,所以今天一上午大部分时间都花在这道题上面了,虽然其中很大一部分原因是因为没有彻底回顾完整代码直接yy 代码流程导致被误导,G。但是这个思考的过程曲折反转,是非常有趣的事情。
【UNR #5】诡异操作
非常 nb 的技巧。
容易想到除法暴力做到底层, & 打标记,线段树上每一个节点维护大小为 logV 的 cnt 表示二进制位上为 1 的数组个数。
这样复杂度是 qlognlogV,具体分析可以看 2022 8 月总结文末。
矛盾在于 logV,可以接受的是 logn
发现我们实际上维护的是 \(logV \times logn\) 的 01 表格,不妨交换行和列,考虑只维护 logn 个数字
此时每一个数 val[i] 的意义是 (出现次数 >> i ) & 1 的二进制位的和。
此时答案就是 \(\sum val[i] \times 2^i\),复杂度降为 qlog^n
实际上,val 的作用在于计算答案而不在于判断是否需要递归,不然会增加代码复杂度。判断是否需要递归可以求区间的数的 or 和 s, 如果 s & v == s, 说明区间不需要修改。
CF526F Pudding Monsters 3000
naive:容易有 n^5 的暴力做法,稍微优化可以 n^4
考虑如何优化,上述暴力做法的不足在于没有利用好题目中的性质
思考之后可以发现,把一个点 (x,y) 视作 p[x] = y, p 显然构成一个排列,题目可以转化成求排列的值域连续区间个数。
感觉之前模拟赛好像见到过类似的问题,当时还觉得解法非常巧妙说一定要记下来,但是...
一个想法是枚举区间最小值的位置,然后求出一段极长区间满足区间内最小值都是他,然后可以递归做?
那么如何计算当前位置的贡献呢?一个 naive 的想法是双指针,简单分析之后发现实际上这里并能使用双指针,合法的区间不一定是包含的关系。思考暴力做法,上 DS 复杂度也降不下来。
上述做法的不足在于没有利用已确定的最小值。设钦定位置 i 是最小值,那么若 j > i 条件是 p[i]-i=p[j]-j,左边是定值,所以上一个主席树应该就可以了。j < i 条件是 p[j]+j=i+p[i],也可以做。
上述的做法每一个点实际上都会做一遍,所以实际上也不需要所谓递归。
问题在于,这个计算的是以当前点为区间端点且为最小值的情况,没有计算到所有以这个值为最小值的区间。但其实修一修也是可以做的。比如 j > i 的情况,需要计算出 j 作为 max 的最长向左延申的距离,容斥容斥即可。
c,发现不对了,j 可不一定是这个区间最大值的位置, 所以上面那个判定条件是错的,寄摆。
一个想法是 CDQ 分治,发现两端的信息合并好像有点复杂?讨论也非常复杂,不会了。
Hs_Black 总结了另外几种很巧妙的方法,所以 CDQ 实际上是可以做的,对于讨论复杂的问题,只需要钦定哪一侧最大然后正反做两遍即可。
感觉这种正反做两遍的手法很巧妙啊,直接化解了冗杂的分类讨论。
视频讲了一种非常繁琐的做法,大意是转化成矩形然后上扫描线+线段树
CF997E Good Subsegments
思考之后发现这道题不能够套用 CDQ 的做法,区间询问过于难缠。考虑扩展线段树做法,显然先离线,不同之处在于可能一个合法区间会贡献多次,具体贡献次数为其合法的时刻到现在的时刻,所以每次打一个时间戳即可。
9.2
上午一道题写了接近一上午,下午最开始有点困,后面慢慢好起来了。晚上状态还算不错,但是可能是因为确实接触太少了导致对题目没有想法,看似在想题但是并没有想出什么东西来,感觉心情不够沉静,不是非常集中。
晚上偶然点开了 alex_wei 的博客,看了看他的游记之类,觉得非常 nb。
在 NOI 前,当我听到 csy 说自己的目标是重铸江苏荣光时,我顿时肃然起敬。
因此,我更希望大家在这场盛会中收获宝贵的友谊,回忆,成长,与平和的心态,它们不是零和的,它们无价且无穷。
奇快无比的训练节奏让我马不停蹄地向前冲,而忘记回头看看自己究竟抛下了多少珍贵的东西。
我们不是为了更好的生活而学 OI,而是因为 OI 而更好地生活。
CF464E The Classic Problem 3000
想到一个 trick 是取 log,但是这里的运算并不是乘法,所以只能是硬艹
需要实现的操作是高精加和大小比较,特殊性质是边权都是 2 的次幂。
容易考虑二进制,看成一个 string 之后大小比较可以先求 lcp(hash + 二分)
每次加法是对某一个二进制位加,影响是把后面的一段 1 赋值成 0,下一个 0 变成 1。考虑用线段树完成这个操作,线段树上维护 hash, cnt 表示区间 hash 值和 1 的数量,那么大小比较和加法都可以通过线段树上二分实现。
问题在于不可能开 n 棵线段树,每次修改量小,所以使用主席树。
问题在于区间赋值难以在可持久化结构上完成,具体原因可以见 2022 HL 总结 Day1 树上问题 小根堆。处理方法是建立一棵全部为 0 的线段树,把儿子接过去即可。
dij 的时间复杂度是 \(\mathcal{O}(\alpha mlogn + \beta m)\) , \(\alpha\) 是大小比较的复杂度,\(\beta\) 是加法复杂度,总复杂度可以认为是 \(O(nlog^n)\) 因为 n, m 同级。
空间复杂度在于加法的时候需要新建,\(O(nlogn)\)
写了 1h+ ,调了接近 1h20min,唯一的错误是 hash 值冲突,srds,代码能力 rising
(另外一个影响常数的错误是双向边导致已经确定的 v 会被再次遍历)
CF603E Pastoral Oddities
naive:
感觉这个限制很奇怪啊,要求每个点的度数是奇数。
随便画了几个图,思考了一下,感觉合法的图应该是有偶数个点。
画画图感觉了一下,度数应该存在某种传递关系,奇数点总是存在消不掉的偶度数,那么只能往外面传,而一次要么同时干掉偶的度数,要么加上一个点,所以感性一下就是不行。
稍微理性一下,奇数个点的话,那么总度数是奇数,这显然是不对的。但是偶图也不一定行,比如四元环,因为度数和也可以是偶数乘上偶数而来。
题目转化成了选择偶数个点,每个点都选择边权最小的连向其他选择点的边,问选择边集中最大边权。可以贪心地发现最后选出来最好是匹配的形式,这样应当是不劣的。
考虑最大最小怎么办,这很重构树,但是是动态的,而且希求取次大值,先上一个 LCT 维护最小生成树森林再说。
能取到次大值当且仅当固定最大值边不选之后存在匹配,即最大值边不是必匹配边
去复习了一下二分图必匹配边,发现大概是没有扩展性的/qd
思考了一下,发现难以从一棵生成树里面抽出匹配来。发现好像不一定非要是匹配的形式,即上面的猜测是错的,因为这个生成树显然是一个解(考虑若某一个点有偶数个儿子,那么此时这些儿子需要在子树中增加奇数个点,但是这些儿子显然只能增加偶数个点,不然不满足条件),但是难以从生成树中抽出匹配来,而生成树是最小生成树。
会不会存在某些情况下可以形成匹配,但是生成树不行的情况?不会,所以实际上的结论应该是最小生成树就是不劣的。
所以 LCT 维护最小生成树即可。
sol:
上面的想法大致思考是对的,但是可能由于是下午脑袋不清醒,有些地方在乱糊。
感觉这道题非常有趣啊,题解大致有三种思考,一种是上面的维护最小生成树森林,即用 LCT 代替 kruskal;一种是发现答案单调性,那么可以整体二分,这个思考在一类决策单调性的题目中也有,例如 2022HL集训 传统 ;还有一种线段树分治,对于题目的分析更加独到,主旨在于注意到一条边有用的时间是一个区间。
这里用自己的话说一说这个线段树分治的做法:
偶点数的生成树肯定有解,构造是一个节点有偶数个叶子就向父亲连边。所以一个多个偶联通块是充要的。
首先观察到答案显然是单调不增的,考察静态图的解法(排序边,并查集维护奇联通块个数),发现一条边有贡献的时刻是一段区间。也就是说,一条边在某一个时刻 t 之后,时刻 t + 1...m 都存在不使用这条边的更小答案。
暴力的方法是排序边之后,逆序枚举时间 t,使用静态图的解法得到 t 的答案,然后把时刻为 t 的边回滚,计算 t - 1 的答案。若在某一个时刻 x 加入边 e,那么边 e 的影响范围的结束点就是 x,因为 x + 1...m 时刻不需要他。
考虑使用线段树分治优化这个过程即可,分治的时候先递归右区间即可。
CF1446D2 Frequency Problem (Hard Version)
思考非常充沛的一道题目
最开始感到非常蒙蔽,感觉没有下手的空间啊,盯了下样例但是什么都没有思考出来。
考虑答案会长成什么样子,发现答案可能长得奇奇怪怪,并没有想到什么性质。
看了一眼题解,c,答案长得不奇怪,答案必包含全局众数。想了一会儿证明,最开始正面思考没有用,考虑反证法,但是不是很会扩展这个区间。
证明是考虑一步步扩展 \([l,r]\to[l-1,r]\to[1,r]\to[1,r+1]\to[1,n]\) ,中间必有一步满足全局众数和局部众数出现次数相同。
进一步思考,先考虑 Easy Version,观察数据范围,复杂度应当和值域相关,那么枚举第二众数,转化为一个只包含 -1/0/1 的序列求最长的区间满足和为0。
这很像二分中位数的 trick。这样是有正确性的,虽然可能算入不和法的贡献,但是不会算多且一定算对。
回到 Hard Version,又不会了,看了一眼 hint,发现最后复杂度是 \(n\sqrt n\),稍加思考之后得到了根号分治的做法。
在对根号分治做法中 出现次数 \(\le \sqrt n\) 的部分 的思考中发现只需要关注与第二众数相邻的全局众数,那么实际上利用 set 找前驱后继可以得到 \(O(\sum occ \log n)\) 的做法,也就是 nlogn
看题解,发现上述两个想法都是正确的,但是还有一种 \(O(n)\) 的做法,花时间但是有点半懂不懂。
upd 9.3 感觉懂了,非常巧妙的做法!题目性质分析非常独到。
9.3 模拟赛
早上有点起不来,虽然人是比较清醒的,但是感觉身体的每一个细胞都在呼喊着摆烂,催促我快躺下。
想到今天是开学前最后一场模拟赛,明天就要开学但是没有做任何的假期作业,那么还是要调整好状态认真对待,强迫自己来到学校。
路上不断调整状态,但其实已经有了自怜自艾的感觉了,感觉心里面有点憋屈。
在电脑面前坐下的一瞬间感到难受的情绪达到顶峰,难以接受马上就必须要进行高强度思考的事实,强行按下不耐,开始读题。
T1 想了一小会不会,最开始以为区间 DP 但是不大行,感觉是结论题。
T2 一脸懵,快速糊了一个三分,但是感觉构造单个子矩形固定大小的值有点奇怪。
T3 感觉非常可做,感觉应该是小清新 DS。
T4 暴力很好做。
然后感到心态有点爆炸,负面情绪已经压制不住了,在窗边看了 30min 风景,交给大脑自动思考。
9:00 回到机房,开始模 T1 样例,希求某种贪心策略,使用接近 1h10min 找到了一个认为正确的贪心,放着不想写。
T2 感觉有点 DP 的味道,感觉应该是可以把矩形切开来做之类,但是不想细想。
T3 不懂啊,只会暴力。
其实进入思考状态之后,感觉并不会有什么情绪上的影响,唯一的影响可能是看风景的时候的胡思乱想限制了后面的思考过程。
感觉考完的时候已经完全没有什么负面情绪了,那么这次小爆炸就算是过去了把。
T1:结论题,重心放在找性质
T2:因为负面情绪导致那个“矩形切开”的正确做法被忘记了。
T3:因为负面情绪导致问题转化步子迈的太大,被暴力禁锢了。巧妙的 DSU on Tree。
T4:没有时间看,时间分配有点小问题,但是今天情况不同可以理解。
中午打球很高兴导致没有午睡,下午强撑着看懂了 T123 写出了 T3,但是这个强撑却是和之前的强迫有本质的区别。之前是不愿意但是知道应该做,现在是求知之心霍霍燃烧。
9.6
差不多一直在看 78 月份的总结,看的过程中尽量做到自己能够回顾出来,思考之后再看,虽然还是有些地方依旧没有搞明白,但是感觉非常有收获,看到了这两个月自己的努力和一些成长。
9.8
口胡了一下 CF723F st-Spanning Tree 然后 1h+ 写出来了
在和 zqm 聊我口胡的 CF575G Run for beer 过程中不断修锅,得到了复杂度较劣的解法,大致是分层+找到那些存在用 0 直达 T 的点+多源 BFS+主席树判断大小
主席树判断大小的方式类似 CF464E The Classic Problem 3000, 此处操作仅仅是插入一位新的数字,所以可做。
那么这样的复杂度是 mlogn 的,非常不优雅。去学习了题解的做法,很妙,时间原因选择了题解做法。
9.20
咕了好久/jk
由于竞赛的时间太少了,导致没有写总结的心情。
今天主要是在看题,没有写,这里记录一下 wyb 对于部分题目的想法。
CF1051E Vasya and Big Integers
有点抽象,考虑暴力 DP,稍微优化配合 hash+二分 秒了
CF1310C Au Pont Rouge:
延续主席树求第 k 大的思想,一位一位确定。需要计算最小划分出的字串 <= S 的方案数,容斥一下算 >,容易想到一个 n^2logn 的 DP,log 来源于二分 dp 刷表的边界。这样总复杂度是 n^3log|\sum|logn,非常不行。
sol: 发现一个瓶颈是主席树求 K 大的思路导致计算了非常多重复冗杂的部分,比如用 DP 计算了 >abcd 的方案数,又计算了 >abce 的方案数。实际上直接二分答案就好了。
需要对子串进行排序,直接 string 不太可取,可以 n^2 递推出 lcp,那么做到 n^2logn 排序。
总复杂度 nlog^2n,实际上 DP 里面那个 log 是可以优化的,既然知道 lcp 数组,没有必要再二分求一遍了。总复杂度降到 nlogn
CF477D Dreamoon and Binary
naive:题目显然可以转化为划分为不降的段,可以使用线段树计算两段之间 +1 操作的数量,那么 DP 就好了?
sol:问题在于取 mod,所以直接 DP 不行,同时实际上没有必要使用线段树,总的 +1 就是最后一段数字大小。
发现最后一段段长 <= 14,否则 +1 次数巨多,那么枚举最后一段长度,然后进行 dp,发现 dp 过程不用 mod
CF1562E Rescue Niwen!
naive:求出排序后每一个字串后面第一个比他大的且左端点不同的字串,并链接一条边,这个可以 n^2 预处理出 lcp 之后二分查找。向 \([l,r+1]\) 连边。
形成了一个 DAG,跑一个 DP 就好,总复杂度 n^2logn
仔细想想好像二分查找不太行,左端点改变之后单调性有问题。可以考虑枚举 l,对于 [l,l+1...n] 的字串一起找第一个比他大的,即双指针。可以 o(1) 比较字串大小,所以复杂度 n^2
ccc,好像 G 了,还是要二分,复杂度 n^2logn,考虑到 \(\sum n = 10000\), GG
sol2: n^2,很聪明的做法。
CF1701E Text Editor
naive:第一部分肯定是不停左移,遇到要删的就删去;第二部分则是跳到开头,然后右移并删去要删的.
可以想到做正反两次 DP 然后枚举中间点合并。问题在于可能中间一段不需要删,不知道怎么处理了。
hcyhcyhcyhcy 通过巧妙的 DP 转移解决中间段相同的问题
CF1363F Rotating Substrings
naive:只会判无解/jk。一个思考是把这个问题转化为排序的形式,然后想要尽可能地消灭逆序对。
sol:神仙题。每次操作等价于把一个字符往前移动到任意一个位置上。那么,假设当前使用 \(s[1\dots i]\) 匹配 \(t[1\dots j], j\ge i\), 那么肯定是取了 s 串后续的 j - i 个对应字符,且实际上清楚到底是 \(1\dots j\) 的那些位置发生了这种提前借字符。
也就是说,对于一个状态 (i,j) 而言,我们实际上已经记录了具体的匹配情况了。
设 \(f_{i,j}\), 那么若 \(s_i = t_j\), \(f_{i - 1, j - 1} \to f_{i, j}\)
否则,如果 s_i 可以借(先判无解,那么肯定可以借),\(f_{i, j - 1} + 1 \to f_{i, j}\)
有借有还:\(f_i{i - 1, j} \to f_{i, j}\)
一个问题是如果虚空还钱怎么办,即本来是不需要还的位置还了。实际上这种情况是不会对最终答案产生贡献的,因为这个状态 \(f_{i,j}\) 显然是不合法状态,也只能对不合法状态产生贡献(因为强制还的操作等价于说是把一个不需要往前面移动的字符移动了,而前面本来是匹配好的状态,被打破了)
CF696D Legen...
板子,AC 自动机上矩快优化 DP。
9.21
zqm 居然要退 OI,感到很惊讶,产生了很多新的思考。
前面 20min 在看字符串,后面开始和 zqm 聊,然后开始思考自己的人生,然后做 whk 去了/qd
9.22
感到非常疲惫,想做一点更令人感到惊奇,拓宽认识的事情。1h
感到非常困倦,睡了 20min+,醒来之后感到自己今天晚上有必要好好休息一下了。
做了一会儿 whk,和包老师聊了聊,感到有了新的视野。
9.23
写了 CF1310C Au Pont Rouge 和 CF1051E Vasya and Big Integers,感到自己的代码能力退化得有点严重了,二分都不会写了,还犯了很多奇奇怪怪的错误。
CF477D Dreamoon and Binary 没有写完。
9.24
打了一中午的球,没有睡觉,导致下午没有精力了
发现 CF477D Dreamoon and Binary 题读错了,调了半天 RE,发现访问了 -1。
然后发现想得 naive 了,我的做法假掉了,我直接裂开,虽然知道怎么改但是不想写了,放弃这道题了。
9.25
写了 CF696D Legen..., 发现自己 AC 自动机可以说是忘完了。
[SNOI2019]字符串 一眼 SA 模板题,发现是普及+,原来有更聪明的做法/qd
来到 CF1562E Rescue Niwen!, 修了修之前的想法,以为复杂度降下来了,写了一点发现不太对,复杂度还是 n^2logn,GG
9.26
写 20201114树上的数(tree), 第一眼以为可以直接上一个 dfn + BIT 走人, 写完发现涉及到区间覆盖操作,需要线段树,GG
挽救的方法是 BIT 维护子树内部存活点数,再来一个 BIT 专门维护这个点有没有被删。
被题解妙到了,注意到如果一个点被删过了,那么删它父亲的时候显然没有必要再删一遍,而且每次如果遍历到这个点,那么这个点一定是要删的,所以复杂度 O(M)
20201114时代的眼泪(tears),想到一个主席树+换根 nlogn 做法,卡了一会儿空间,总体很 nice。
9.27
因为已经学习了 pt 的博客, 所以会做 20201114传统艺能(string), 一遍过。
被 [CF643G]Choosing Ads 打爆了,以为不带修,想了一个 nlog^n 主席树上二分的做法。发现带修,那不会了。没有注意到一个关键性性质是 \(p\le 20\), 即最多输出 5 个数字,所以可以上摩尔投票。
9.28
写了 [CF643G]Choosing Ads, 奇怪的 RE,然后发现是变量名写错了。然后发现清空 Tree 节点的时候没有把 tg 清掉。
看 Dev please add this, 非常 naive 地以为直接连边然后缩点就可以了。但是实际上涉及到一个选和不选的问题,这种问题可以使用 2-sat。没写代码
看 CF1312G Autocompletion,最开始把题意看错了,看对了之后先上了一个“虚树”,然后探究到自动补全两步和分开做是等价的。但是没有注意到朴素转移方式的影响,GG。正解也不难,被骗了。
从今天的整体来看,感觉对题目的考虑实在是不够完善,可能是因为没有集中注意力的原因。
9.29
被 CF1366G Construct the String 欺骗了,轻松得到了一个 \(O(n^2)\) 的算法,考虑到 n = 1e4 觉得可能难以通过,思考优化。想了半天觉得这个实在是没有什么优化的途径了,看题解发现 n^2 就可以了/kl。
我的想法是先标记一下 s 串上每个字符是否处于被删除的状态,然后对去掉 '.' 的 s 做 dp。c,写了一小下发现细节太多了,还是题解做法优雅。
仔细想了一想发现我这个做法实际上和题解是一样的。上面细节的地方就在于可能存在后面一段 '.' 删完自己前面一段之后还可以删,这个时候需要转移的点是第一个“收支平衡的地方”,等价。
20220921 彩票,注意到奖项独立的性质之后就可以暴力枚举了。
20220921 战斗。非常 nb 的题目,只会 n^5 暴力区间 DP/qd,学到了一类区间 DP 的优化技巧。
实在是被 战斗 震撼到了,联想到了 Pre-Order 等题目,有了更深的认识。