总结
-
2025.7.8
上午把分治往后备了一点,学习了根据最值分治的套路,下午在消化昨天的内容,把线段树的半群模型入了一点门.
晚上听课,把之前一直不明白的回滚莫队和二次离线搞明白了,还提出了关于奇偶化排序的一个问题,和大家讨论后收获不小.
课后做了回滚莫队的模板,又去补了几道之前没过的题
根据最值分治:
式子中有最值,要求计数或算贡献,考虑以区间最值为分治中心,计算跨区间贡献,来固定式子中的一项,被子区间完全包含的递归处理,注意边界处理.
由于分治中心分出的两个子区间长度不定,所以采用启发式分治的思想,枚举长度短的一侧,计算与另一侧的贡献. 时间复杂度 \(O(n \log n \times f(n))\), \(f(n)\) 为处理跨区间贡献的复杂度
2025.7.10
不知道
2025.7.18
2-sat
k-sat 是 npc 问题,不可做
2-sat bool 变量可达性问题
用 tanjan 缩点做到 \(O(n+m)\) 的复杂度
定义 :
-
\(i\) 代表 \(x_i\) , \(i+n\) 代表 \(\neg x_i\)
-
当 \(x_i\) 成立时 \(x_j\) 一定成立,称 \(x_i\) 蕴含 \(x_j\)
-
蕴含具有传递性,用图论建模解决
连边:
当 \(x_i=0\) , \(i \to \neg i\)
当 \(x_i=1\) , \(\neg i\to i\)
当 \(x_i\vee x_j=0\) , \(i \to \neg j\) , \(j \to \neg i\)
当 \(x_i\wedge x_j=1\) , \(\neg i \to j\) , \(\neg j \to i\)
算法流程:
如果 \(x\) 可达 \(y\) ,则 \(x\) 可推出 \(y\) .
如果 \(x\) 与 \(x+n\) 在同一强连通分量,无解.
通过互反变量的拓扑序大小判断变量的真假,从而构造方案.
例题
\(x\) 和 \(y\) 有连边 , \(\neg x\to y\) , \(\neg y \to x\)
\(x\) 和 \(y\) 在同一部分,\(x\to \neg y\) , \(y \to\neg x\)
前后缀优化建图,使边数降低至 \(O(n)\) 级别.
8.31
P7078
贪心的想,一条蛇在什么时候会选择结束游戏。
当最强蛇吃掉最弱蛇后不会变为最弱蛇,新的最强蛇的长度一定没有原来的长,最短蛇一定没有原来短,所以新的最强蛇无法吃掉之前的最强蛇。这时他就可以放心地吃。
如果他变成了最短蛇,考虑下一条最强蛇是否敢吃他,如此递归考虑,直到出现了一条可以放心吃的蛇,再回推出当前最强蛇是否敢吃即可。
用 set
维护蛇的长短,依照上述过程模拟即可。
时间复杂度 \(O(n \log n)\)
P5021
首先看最小值最大想到二分答案,考虑如何判定是否合法。
在一个结点的路径,有三种归宿,
其一为与子树内的其他路径喜结连理,共同组成赛道。
其二为向上延伸,与更高的子树内的路径组成赛道。
其三为打一辈子光棍。
我们首先让子树内尽量配对,再在剩下的路径中选一条向上延伸,最后判断路径条数是否达标即可
时间复杂度 \(O(n \log^2 n)\)
9.1
P8632
简单斜率优化,先写出朴素DP方程 \(f_{i,j}\) 表示前 \(i\) 个村庄修建 \(j\) 个会点的最小距离
其中 Val 为 贡献系数
直接拆成直线解析式的的形式,因为第二位只有 \(4\) 种取值,所以我们直接用 \(4\) 四颗李超树维护就行了。每次从 \(j-1\) 那颗树上转移就行了。
时间复杂度 \(O(n\log n)\)
拉格朗日插值
P7116
考虑把所有点向某个方向的行为看成这个场馆往反方向平移。每次操作后答案加上当前剩余的点数。显然没次剩余的点数都应该是一个 \(k\) 维立方体。其中有一维为 \(0\) 时退出。
我们把每一维的变化量 \(\Delta\) 暴力算出来,并将它取整,这样左端点不再变化,只有右端点变化,且
长度的变化量为一个关于轮数的一次函数,那么答案就是每一位长度乘积的前缀和,是一个 \(k+1\) 次函数,我们暴力求 \(k+1\) 轮的答案,作为点值插值,求出答案的函数,代入一个最小轮数就能处理出所有整轮的答案,最后在暴力跑剩余的部分即可。
9.2
模拟赛
A
若至提,考虑对区间造成贡献的只有最值,直接按最值分治,考虑有多少种区间长度,用差分维护一下就可以了。 复杂度 \(O(n\log n)\) (考场上写了棵线段树凭空多了一只 \(\log\))
讲了 \(O(n)\) 做法,只用差分就行了
B
一个直观的想法就是比较组合数的大小,但是太大了,存不下。
所以我们可以通过比较 \(\log\) 的大小来比较原数的大小。
这样就有了一个 \(O(n^2)\) 的做法。考虑优化,发现在选的数一定时,总数越大,结果越大。
所以我们先只在 总数为 \(n\) 的组合数中选择,选走一个组合数后再将总数-1 加入。
时间复杂度 \(O(k)\)
C
中间忘了
D
把每个事件向后面第一个难忘的事件连边,可以构成一棵树的结构,用树上主席树维护 \(val\) 即可,
时间复杂度 \(O(n \log n)\) 。
9.3
P3960
小清新DS题,观察题目发现离队操作只会影响当前行和最后一列的队形,所以考虑用数据结构维护每一行和最后一列的信息。
考虑我们需要怎样的数据结构,单点插入/删除,全局 \(k\) 大值,我们可以选择用平衡树暴力维护,但不好写,常数又大。考虑动态开点线段树,它可以完成除了插入之外的操作,但由于本题的插入只在尾部进行,所以我们可以在尾部多开一倍空间,插入就直接向后插入,其他操作简单维护即可。
但初始信息是 \(n^2\) 级别的,直接初始化会爆炸,所以我们定义空节点为初始状态,只有被删除或后来插入的信息建立节点,这样的复杂度是 \(O(q \log n )\) 的可以通过此题。
P3959
状压DP,容易发现最后的答案会形成一棵树,而一条边的花费与它起点的深度有关,
所以可以设 \(f_{dep,S}\) 为树深为 \(dep\) ,已选集合为 \(S\) 的最小花费。
容易得到转移方程 \(f_{dep,S}=\min_{T\in S} f_{dep-1,T} + dep * cost(T,S)\)
其中 \(cost(T,S)\) 为将集合 \(T\) 转移为 \(S\) 需要连的边的边权。
枚举起点,依照上式转移即可,时间复杂度 \(O(3^n n^2)\)
P4001
算法:图论建模(平面图转对偶图), 最短路
观察题面,题面给了一个网格图,还带边权,显然是最小割,可以转成对偶图然后跑最短路,跑完之后就是最小割,具体的:给每个格子编号,确保网格图中的边每条都恰好被一条对偶图中的边切,对偶图中的边权就是所切的网格图中的边的边权
注意:看清对偶图中的终点的编号是什么!!!
9.4
P1600
树上差分,考虑什么样的节点可以被统计到,令一条路径的起点和重点分别为 \(s\) 和 \(t\) 。
当 \(s\) 为 \(t\) 的祖先时,满足 \(dep_x-dep_s=w_x\) 的点可以被统计到,
当 \(t\) 为 \(s\) 的祖先时,满足 \(dep_s-dep_x=w_x\) 的点可以被统计到,
否则,令 \(s\) 和 \(t\) 的 lca 为 \(p\)
当 \(x\) 在 \(p\) 到 \(s\) 上时,满足 \(dep_x-dep_s=w_x\) 即可
否则 满足 \(dep_x+dep_s-2*dep_p=w_x\) 即可
把相应的深度插入对应的线段树,用线段树合并做一个类似前缀和的东西就行了。
时间复杂度 \(O(n \log n)\)
P4175
循序渐进的考虑问题,如果在序列上,直接主席树即可,搬到树上,用树上主席树维护根链信息,树上差分维护即可,如果带修的话,可以树剖+树套树 在 \(O(n\log^3 n)\) 的复杂度内解决。
考虑更优的解法,由于只维护根链信息,所以树剖是不必要的,可以尝试用树上差分解决,
考虑增加/减少一个数,会对它子树内的根链信息造成影响,所以我们需要一个支持子树修改,单点查询的数据结构维护,子树修改可以用 dfn 序拍成区间修改,于是我们就可以使用树状数组套线段树+差分以 \(O(n \log^2 n)\) 的复杂度解决此题,据说还有神秘单 \(log\) 算法。
9.5
P10375
简单题,求一个序列的中位数可以线段树上二分,题目要求求两个序列上的中位数,我们直接对每个序列都开一个线段树,询问时将询问的两颗线段树一起二分就行了。
注意离散化一下,要不然空间复杂度会达到 \(O(n \log n)\) ,会被卡。
P3730
要求求区间内出现次数的 \(k\) 小值,发现单纯使用主席树不是很好差分,所以退而求其次,考虑单根算法,用莫队维护区间出现次数,用值域分块维护 \(k\) 小值,选用 \(O(1)\) 修改的值域分块,根号平衡,总时间复杂度 \(O(n \sqrt{n})\) 。
注意特殊考虑区间内出现次数为零的数。
P4867
发现要求维护的信息不可差分,考虑根号算法,用莫队套值域分块,只在出现次数由零变一或一变零是向值域分块中插入/删除元素,查询操作相当于在值域上的求和,是容易做的。根号平衡后时间复杂度为 \(O(n \sqrt{n})\)。
注意不寻常的空间限制,调整块长以降低空间开销。
双倍经验 : P4396
9.6
A P3760
看到异或运算,考虑按位考虑贡献,用前缀和优化,化一下式子。
考虑减法的按位意义,发现与借位有关,分 0/1 的情况讨论,用树状数组维护即可。
B
不知道
C P5443
没有修改操作时是 kruscal 重构树裸题,有了修改操作不是很好维护,考虑根号算法,根号重构,对每 \(sqrt{n}\) 个询问进行一次暴力重构,将边按权值排序后,将无修改的边加入边集,修改的边将最新的权值加入,用可撤销并查集维护这个过程。
9.7
P3730
要求求区间内出现次数的 \(k\) 小值,发现单纯使用主席树不是很好差分,所以退而求其次,考虑单根算法,用莫队维护区间出现次数,用值域分块维护 \(k\) 小值,选用 \(O(1)\) 修改的值域分块,根号平衡,总时间复杂度 \(O(n \sqrt{n})\) 。
P6406
题目要求维护的是一个 \(\min\) ,\(\max\) ,\(len\) 的乘积的和,遇到这种带最值的序列统计贡献问题一般会考虑分治,我们对区间进行分治,在每层计算跨过区间中点的贡献。
我们令 \(\min\) 的取值一定来自左区间,枚举一个 \(i\) 作为区间左端点,同时分别维护 \(j\) ,\(k\) ,分别为满足 \(\min\) ,\(\max\) 都来自左区间,和只有 \(\min\) 来自于左区间的最右端点。
右端点在 \(mid+1\) 到 \(j\) 的贡献是好算的,因为 \(\max\) ,\(\min\) 都已确定,区间长度是一个等差数列,高斯求和就行了。
考虑右端点在 \(j+1\) 到 \(k\) 的贡献,发现是一个类似
的式子,其中 \(i\) 相关项均为常数。我们只需要再维护一个前缀 \(\max\) 与位置乘积的前缀和数组就可以 \(O(1)\) 解决。
\(\min\) 在右区间的情况是一个相似问题,相似处理就可以了。注意溢出。
时间复杂度 \(O(n\log n)\)
TTM
可持久化线段树+标记永久化 板子
在区间被完全包含时,仍然正常打标记,但不下传,在查询时加上一路上的标记就行了。
9.8
P7475
题目与前缀有关,联想到 trie
树 ,发现就是求子树中的 \(k\) 大值,有两种做法,一种是直接用树上主席树,每个节点的的树都维护子树信息,修改就了修改根链,由于题目给出的链长度不超过十,所以是对的, 时间复杂度 \(O(n \log n)\)
还可以直接用 dfn
序拍扁成区间问题,直接上树套树就行了,时间复杂度 \(O(n \log^2 n)\) 。
9.9
P5522
用线段树维护满足每个区间要求的字符串,合并时分情况讨论,第 \(n+1\) 位表示是否存在合法字符串,双半群合并即可,时间复杂度 \(O(nq \log m)\) 。
不是正解,但哥们常数太小了,直接过了。
P8250
从零开始的根号生活,发现这个共同邻居很难维护,同时整张图的度数是一定的,于是考虑对度数根号分治,先说说暴力怎么写吧。
暴力一 :暴力扫两个点的邻居,打标记判重即可。时间复杂度 \(O(q m)\) 其中 \(m\) 为度数
暴力二 :维护每个点的邻接点的 bitset
,询问时直接取交处理即可,时间复杂度 \(O(\frac{nq}{w})\),空间复杂度 \(O(n^2)\)
我们发现暴力一与度数有关,暴力二的空间复杂度有些爆炸。
于是我们按度数将点分为轻点和重点,询问到轻点直接遍历,重点用 bitset
预处理,时间复杂度很对。
9.10
模拟赛,写写会的题吧。
A
发现当删除某个数时,其他的数的操作次数只可能增加,不可能减少,而每次一定是取最大值操作最优,所以我们倒过来做,用线段树维护一下最值即可,时间复杂度 \(O(n\log ^2 n)\)
P7811
根号分治好题,这种取模的题一般就是根据模数的大小分类了,
考虑 \(k\) 较小时,我们可以预处理较小的所有模数序列,做 \(RMQ\) 即可(因为卡空间,所以用分块做到 \(O(n) 空间\))。
考虑 \(k\) 较大时,模数的循环节较长,所以我们可以在循环节上做文章,
我们可以跳循环节,用莫队维护区间,找到对应区间后用 bitset
暴力跳循环节以及每个循环节的最小值即可。
卡常卡司了
9.11
P13925
合成大西瓜,考虑 \(dp\)。枚举右端点 \(i\) 与左端点 \(j\),如果 \(i\) 到 \(j\) 可以合并就 \(dp_j+1→dp_i\) 。
考虑固定 \(i\) 时如何快速枚举 \(j\)。注意到假设相邻两个合法的 \(j\) 分别为 \(k′\),\(k\),那么必有从 \(k′\) 到 \(k−1\) 合法且这一段合并的结果与 \(k\) 到 \(i\) 相等。
于是用一种类似倍增的方法,记录可以从哪里过来。容易发现这样复杂度是对的。最劣的情况大概是全相等的时候,转移次数 \(O(n \log n)\)。
CF1436E
绝世好题啊,考虑 \(mex\) 的求法,发现极短的 \(mex\) 段最多是 \(O(n)\) 级别的,证明大概是这样的 :
枚举值域,每个数可能作为 \(mex\) 的区间一定不包含他本身,所以序列就被划分为了 这个元素出现的个数+1 段,对所有这样的区间计数,其总数就是 \(O(n)\) 的。
也就是说,我们只对这 \(O(n)\) 个区间求 \(mex\) ,就能得到全局所有子区间的 \(mex\),最后再对 \(mex\) 集合暴力求一遍 \(mex\) 即可。
维护过程如下:
我们维护每个数上一次出现的位置,每次扫到这个数时,就把 \((las_{a_i},i)\) 这个区间的 \(mex\) 求出并记录。同理我们再倒着跑一边,记录每个数到它后一次出现位置的 \(mex\) 。这样就能统计所有子区间的 \(mex\) ,最后扫一遍值域求 \(mex\) 的 \(mex\)
中间求区间 \(mex\) 可以使用主席树 ,时间复杂度 \(O(n \log n)\) 。
相同 \(trick\)
9.12
进行一个初赛的考,知识点如下:
g++
编译命令 :
-
显示编译过程中的详细信息
-v
-
启动优化选项
-O2
-
指定输入可执行文件的名称
-o
费马小定理 需满足 p 不是 a 的因数
哈希的 base
选择:在模意义下大于值域,最好为质数,但不一定要是。
欧拉图(含欧拉回路)所有顶点的入度等于出度
只有欧拉路径的叫半欧拉图。
用数列求和算复杂度时,不要再算循环层数,最好找到类似的已学算法来推算复杂度。
9.13
おくすり飲んで寝よう
之前模拟赛的题,诈骗题,容易发现每次只需要让较大的减少,较小的增大,一定能保持最优决策,按照这个策略模拟即可。
开局不利
判有无解可以按截止时间排序,如果有所需时间大于截止时间时,说明无法无伤;
考虑如何操作能将伤害最小化,发现相同的时间,推惩罚系数大的箱子减少的伤害更多,所以按惩罚系数降序排序,尽量让每个箱子推完的时间靠后,防止挤占截止时间靠前的箱子。
现在问题转化为如何:
-
计算在截止时间前能推多少次箱子
-
将一段时间标记为已占用
-
找到一个尽量靠后能满足当前箱子需求的区间
我们可以用线段树维护上述操作,每个时间段为 \(1\) 代表已占用,否则为未占用。 计算推箱子次数相当于求出截止前有多少个时间没被占用,用总共的减去占用的即可,可以用区间求和解决。2 操作就相当于区间赋值,3 操作可以二分求得,因为区间越靠前,可用的时间段一定不会减少。综上,我们实现了一个 \(O(n \log^2 n)\) 的算法维护此过程。
二分好像没办法搬到线段树上,所以多一只 \(\log\) ,理论可过。
9.14
爬行的 GCD
数学题?发现只要元素的总和大于长度为 \(n\) 的某个理想序列,那么就可以不删元素,只凭加减来变成理想序列,所以问题转化为总和最小的理想序列,显然是由纯质数组成的序列,所以提前筛一下质数,枚举序列长度即可。
爬行合并
首先考虑简单的暴力,容易发现将每个串向组成他的串连边,这是一张 DAG ,在拓扑的时候判断答案应该在哪个儿子里,直至到了 0 或 1。
考虑优化,用倍增和重链剖分的思想,求出每个串只走较大的一边所到的串有哪些,以及到那里需要 查询的位置是多少,每次跳到不能跳时就走一次轻边,走轻边每次串长至少减少一半,最多走 \(\log\) 次,内层倍增是 \(\log\) 的,总时间复杂度 \(O(n\log n)\) 。
晚上一个初赛的考,知识点 :
-
ls
查看目录下的内容 -
cd
切换工作目录 -
cp
copy -
-
char
占八位,但是有符号的,范围-128~127
-
不要用 \(r\) !!!
-
放个 LInux 命令表
9.15
P4215
线段树典题,发现孩子的要求无法在每次操作后都扫一遍,考虑如何只遍历操作对其有影响的询问,我们把询问挂在线段树上,每次单点修改时,只检查所涉及的链上的询问,对每个询问,我们维护他在多少个区间中出现,如果某次修改后值为零,则更新答案。
9.16
模拟赛
T1
构造+结论题,打表发现最多操作 \(2\) 次,所以直接分别处理操作次数不同的情况,当逆序对数本就符合要求时显然时 \(0\) ,如果有一个区间的逆序对数等于原逆序对数与 \(k\) 的差,那么这个区间就是答案。当需要操作两次时,我们优先选逆序对较多但不大于原逆序对数减 \(k\) 的区间,第二次选择一个恰好的区间。因为题目保证有解,所以是对的。上述找区间的过程可以使用双指针+权值树状数组解决。
T3
令 $f_{i,j} $ 为是否能做到 \(s\) 匹配到第 \(i\) 位,\(t\) 匹配到第 \(j\) 位,转移有两种情况,一是上下匹配,转移式为
另一种是题目中的插入两个相同的字符,转移式为
这样就写出了 \(O(nm)\) 的暴力,发现转移可以用 bitset
优化,用 bitset
存 \(f\) 的滚动数组和每个字符在 \(s\) 中出现的位置 \(pos\),转移一就优化成了
转移二就优化成了
时间复杂度 \(O(\frac{nm}{w})\) ,可过。
9.17
发现选择集合与顺序无关,所以将 \(a\) 和 \(b\) 升序排列后考虑。
我们并不关心如何让选出的集合合法,那么如果我们已经确定了选择哪些元素,最好的方法就是将 \(b\) 的前 \(k\) 个与 \(a\) 顺次配对,后 \(n-k+1\) 个同样如此。
我们可以设计一个 \(O(n^3)\) 的动态规划,枚举选出集合的大小。
设状态:
转移如下:
由于 \(k\) 是转移所需的参数,必须枚举,因此复杂度过高。
考虑优化,发现正序考虑选入集合和逆序考虑不选入集合是对称的。
我们希望将一个前缀和一个后缀拼接起来,但并不是每个拼接点都合法。
对于每个 \(k\),我们希望拼接点 \(p\) 满足:
因此,第一个满足 \(a_i > b_k\) 的位置 \(i\) 就是拼接点。
9.19
candy
首先有一个重要的性质,每个数模小于自己的数,至少减小一半,所以我们只存每个数下一个比他小的数的位置,倍增就能快速找到答案。
考虑将询问的区间转为前缀和,记 \(S_p(x)=∑x_i=F_p(x)\) ,转移为记 \(q\) 为 \([p,n]\) 中第一个比 \(x\) 大的 \(a_q\),\(S_p(x)=\lfloor \frac{x}{a_q} \rfloor {a_p} S_q(a_q-1)+S_q(x \mod a_q)\),预处理出 \(S_p(a_p−1)\) 后即可 \(\log n \log V\) 完成查询。
9.20
P3988
发现其实就是一个找第 \(k\) 大的操作,然后销牌的操作其实是一个循环位移的过程,维护一个偏移量,表示应该发那张牌,对当前牌库中牌的数量取模即可。
SP2916
分两种情况讨论,分为两端点区间是否相交;
若不相交,答案组成其实就是左边的最大后缀和+中间的和+右边的最大前缀和,用类似小白逛公园的线段树维护一下就可以了。
如果相交,答案有三种可能,一是在中间重合区域直接求最大子段和,二、三分别是左/右端点在各自区间内自由,另一个端点在未重合区域的答案。
9.20
檐牙覆雪
开局没思路,先想暴力,单次 \(O(n \ln n)\) 的做法是显然的,通过打表发现每个点的最佳转移点是他的他除以他的最大质因数,提前预处理一下,可以通过 \(50\) 分的高分。
现在开始想正解,我们把每个点向他的最佳转移点连边,最终是一颗树的结构,每个点的答案就是根链上的贡献。每次转移时顺便做一个子树加就行了,预处理出来就很显然了。
美好的每一天
考虑回文子串的定义,发现回文串中最多有一个字符出现奇数次,考虑有什么东西偶数可以消掉,异或拥有类似的性质,我们发现字符集大小不大,所以直接用一个二进制位代表一个字符,这个东西是经典的莫队问题,前缀异或一下,增量时查询有多少区间满足和当前量异或和为 \(0\) 即可,这样就解决了偶回文串的问题。
考虑奇回文串,我们可以枚举是哪个字符出现了奇数次,查询这样的区间是否存在就行了 复杂度 \(O(26 \times n\sqrt{n}))\)
9.21
模拟赛,哈哈哈哈。
T1
递推基础练习题,式子花费 eps 秒推出,花费 eps 分钟写完矩乘优化,简单。
T2
基础哈希练习题,二分合法长度,暴力 check
即可,为了稳妥写了个双哈希,最后发现大样例过不去,以为是常数太大了,没想到是数组开小了,🤣🤣🤣。喜挂 \(60 pts\) 。
T3
简单差分约束题,但 \(50ms\) ,变成困难 \(DP\) 题,令 \(f_i\) 为 \([1,i]\) 已经合法的最大数量,维护 \(i\) ,位置可以转移的区间,用单调队列优化即可。最后判无解即可。
T4
根号题滚出 oi ,😡😡😡。
先看特殊性质,发现 \(a\) 对 \(b\) 取模后并不影响答案,所以直接预处理出所有 \(a,b\) 对应的前缀和,\(O(1)\) 回答询问即可。
考虑正解的空间限制有限,考虑只维护每个块的答案,\(O(\sqrt{n})\) 回答询问。
预处理过程如下:
定义函数:
若 $ f(x) = f(y) $,令:
情况一:$ c = 0 $
要求:
等价于:
注意:这两个区间不重叠。
情况二:$ c = 1 $
要求:
等价于:
情况三:$ c = -1 $
要求:
等价于:
上述所有限制都是关于 $ t $ 的区间,且满足 $ t < b $。因此可以在值域为 b 的桶上进行差分处理,再通过前缀和快速查询整块内满足条件的对数。
我们成功做到了 \(O(nb+b^{2}\sqrt{n})\) 预处理,\(O(\sqrt{n})\) 回答单次询问的优秀做法。
9.22
模拟赛,一道不会,🤣🤣🤣,☺️☺️☺️,退役吧。
9.23
lucky
考试开始三小时后改交互格式,资本你赢了。
设 $ S $ 为所有 2 的非负整数幂组成的集合,即:
可证明以下核心结论:
当且仅当 $ n \in S $ 时,当前轮次操作者(先手)必败;
当 $ n \notin S $ 时,设 $ w $ 为 $ n $ 在二进制表示下「最低位 1 对应的权值」(即满足 $ 2^w \mid n $ 且 $ 2^{w+1} \nmid n $ 的非负整数),则:
先手取 $ 2^w $ 个时必赢;
先手取少于 $ 2^w $ 个时必输。
当前轮次操作者称为 “先手”,另一方称为 “后手”。
采用数学归纳法,对 $ n $ 的取值进行归纳验证。
-
基例:$ n=1 $
因 $ 1 = 2^0 \in S \(,此时先手需取至少 1 个(仅余 1 个),取后后手无数量可操作,符合 “\) n \in S $ 时先手必败” 的结论;
若假设 $ n=1 \notin S $ 的情况不存在(因 $ 1 \in S $),故基例结论成立。 -
归纳步:设 $ n>1 $,且对所有小于 $ n $ 的正整数结论均成立,证明对 $ n $ 结论成立
设先手在本轮取 $ x $ 个(满足 $ 1 \leq x \leq n $),记剩余数量为 $ n' = n - x \((后手操作时的初始数量),\) w' $ 为 $ n' $ 在二进制下的最低位权值(即 $ 2^{w'} \mid n' $ 且 $ 2^{w'+1} \nmid n' $)。分两种情况讨论:
情况 1:$ n=2^w $(即 $ n \in S $,因 $ 2^w = 2^k $ 对应 $ w=k $)
无论先手取任意 $ x \((\) 1 \leq x \leq n $),由归纳假设:
后手面对的剩余数量 $ n' = n - x $,其最低位权值为 $ w' $,且必有 $ x \geq 2^{w'} $(因 $ n=2^w $ 是 2 的幂次,二进制仅含一个 1,$ x $ 需覆盖 $ n' $ 的最低位,故 $ x $ 不小于 $ 2^{w'} $);
后手可直接取 $ 2^{w'} $ 个,使后续轮次的先手陷入必败态(符合归纳假设中 “非 $ S $ 集合数的必胜策略”);
因此,当 $ n=2^w \in S $ 时,先手必败。
情况 2:$ n>2^w $(即 $ n \notin S $)
需分别证明 “先手取 $ x=2^w $ 必赢” 与 “先手取 $ x<2^w $ 必输”:
若先手取 $ x=2^w $:
此时剩余数量 $ n' = n - 2^w $,分两种子情况:
子情况 2.1:若 $ n' \in S $(即 $ n'=2^k $):}
由结论 1,后手面对 $ n' \in S $ 必败,因此先手赢;
子情况 2.2:若 $ n' \notin S $:}
因 $ n>2^w $ 且 $ x=2^w $,故 $ n' = n - 2^w > 0 $,且 $ n' $ 的二进制最低位权值 $ w' > w \((\) 2^w $ 恰好消去 $ n $ 的最低位 $ 2^w $,剩余 $ n' $ 的最低位权值更高);
由归纳假设,后手需取 $ 2^{w'} $ 个才能赢,但 $ 2^{w'} > 2^w = x $ 且 $ n' < n $,导致 $ 2^{w'} > n' $(后手无法取到足够数量),故后手必败,先手赢。
综上,先手取 $ x=2^w $ 时必赢。
(2) 若先手取 $ x<2^w $
设 $ b $ 为 $ x $ 在二进制下的最低位权值,因 $ x<2^w $,故 $ b < w $;
剩余数量 $ n' = n - x $ 的二进制最低位权值 $ w' = b \((\) x<2^w $ 无法消去 $ n $ 的最低位,$ n' $ 的最低位由 $ x $ 决定);
由归纳假设,后手可直接取 $ 2^{w'} = 2^b $ 个,使后续轮次的先手陷入必败态;
因此,先手取 $ x<2^w $ 时必输。
由基例($ n=1 \()与归纳步(\) n>1 $)可知,对所有正整数 $ n $,原结论均成立。
water
怎么又是根号题,😒😒😒
首先写出暴力,打表观察到答案只可能是 \(1/2\) ,所以直接分成两张图来做,由于要求字典序最小,所以只用维护第一张图,无法放进第一张图的边直接丢进第二张图就好了。
考虑如何维护第一张图,发现其实就是一个 DAG 可达性问题,常见的套路是 bitset
维护,加上根号重构/分治。我们这里采用根号重构,每一定条边直接重新拓扑一遍更新,一个块内的边就用 bitset
,暴力判断是否能加入。
复杂度 \(O(\frac{m^2}{B}+\frac{nm}{w}+\frac{mB^2}{w})\)
9.24
上升子序列
考虑新增一个数时对答案的贡献,其实就是以小于他的数为结尾的上升子序列的方案数,因为相同的子序列只算一次,所以用线段树维护以每个数结尾的子序列方案数,更新时直接覆盖掉旧答案即可,这样可以防止算重,初始为了方便,我们先算上长度为一的子序列,在最后统计答案时减去即可。
TEST_34
看到任意选数异或求最大值,自然想到线性基,但是区间异或操作并不好维护,于是我们进行一个前缀异或,修改时用类似差分的操作维护即可。
接下来我们简述如何查询并证明其正确性。
两个线性基等价当且仅当他们能表示出的集合相同,前缀异或后的数组,可以通过后一项异或前一项得到原值,所以我们查询 \([l+1,r]\) 这个区间内的线性基,就可以得到除了 \(a_l\) 和 \(a_{l+1}\) 之外的所有 \(a\) ,\(a_l\) 可以通过 \(\oplus_{i=1}^{l} b_i\) 来求得,直接将他插入询问取得的线性基,就得到了等价于 \(a_l,a_{l+1} \dots a_r\) 的线性基,在这个线性基查询就可以了。
时间复杂度 \(O(n \log n \log V)\) 。
9.25
跑步
分拆数板子,考虑 dp ,设 \(f_{i,j}\) 为前 \(i\) 个数选出总和为 \(j\)的方案数,容易发现这是一个完全背包求方案数问题,转移方程 \(f_{i,j}=f_{i-1,j}+f_{i,j-i}\) ,注意正序枚举即可。
Calc
继续考虑 dp ,设 \(f_{i,j}\) 为前 \(i\) 个数,在 \([1,j]\) 范围内的权值。答案即为 \(f_{n,k}\) ,转移方程也比较好写, \(f_{i,j}=f_{i,j-1}+j \times f_{i-1,j-1}\) ,但是过不了。
考虑继续优化,发现答案是一个多项式函数,所以直接拉插优化,复杂度 \(O(n^2)\) 。
9.26
子集卷积
没有交集为空的限制就是一个并卷积的形式,考虑如何加上这一限制,我们把每个集合按集合大小分类,每个位置应该受到集合大小相加等于当前集合大小且并集为当前集合的贡献,我们只给 \(f_{popcnt(S),S}\) 赋值,确保不会有错误的贡献被计算,然后做一个并卷积就好了。
然后是模拟赛。
矩阵
诈骗题,除了随机撒点检验都能过,下面是我的方法,
考虑利用前缀和能够快速算出 \(A \times B\) 每一行和每一列的和,把他和 \(C\) 的相应行列比较,大概率正确。
但我们发现将两条对角线互换就能卡掉他,所以我们再暴力检验两条对角线,再随机撒点验证即可。
长野原龙势流星群
贪心题,考虑一个权值最大的点,一定只选他自己,而他的父亲一定会选择他来提高平均值,我们维护这个过程,每次找出最大值,记录答案后将他和父亲合并,循环此过程即可。
全局最值用优先队列,合并操作用并查集维护即可。
9.27
ema
构造题也给我滚出oi !!!😡😡😡
奇数的构造是简单的,发现当一个节点确定时,和其他每个节点的和在 \(\mod n\) 意义下不同,所以直接那这个当边的颜色。直接用 \(n\) 种颜色就行了。
考虑偶数的构造,去掉一个点后直接应用奇数的策略,最后剩一个点,发现之前的每个点都剩了一种颜色,连上就行了。
meruru
回来吧我的三维偏序,我最骄傲的信仰☺️☺️☺️
直接做不好做,单步容斥,转成求无交矩形数,分为八种,直接减去上下左右的(这一部分用树状数组维护一下就行),发现左上,左下,右上,右下被多算了,用树套树暴力二维数点一下就行,轻松过原题。
唉不是,你怎么卡我树套树啊😓😓😓,把二维数点用 CDQ 做就过了。
整数的lqp拆分
没有生成函数的生成函数题。
设 \(f\) 为斐波那契数列, \(g\) 为答案,很容易得到 $g_{i+j}=\sum (f_i \times g_j ) $,意义是在和为 \(j\) 是选择了 \(i\) 这个数,从而乘上了 \(f_i\) 的贡献。
把这个式子写出两项。
\(g_i=\sum f_{i-j} g_j\)
\(g_{i+1}=\sum f_{i-j+1} g_j\)
上下做差,得到 \(g_{n}=2 g_{n-1}+g_{n-2}\)
这不是我们矩阵加速板子吗?🤣🤣🤣
9.28
棘手的操作
操作好多,但是好板。
看到连边操作,自然想到线段树合并,发现线段树合并+并查集维护集合能解决除了全局操作的所有操作,太强了。
考虑如何维护全操作,我们可以用一个 set
维护每个连通块的最大值组成的集合,查询时取最大即可,修改操作在线段树操作后更新一下就可以了。
忽略并查集复杂度 时间复杂度 \(O(n\log n)\),唉不是这个 set
常数好大啊。不是题解区怎么都是可并堆和离线。
小众。
9.29
捉迷藏
线段树+性质题,一个经典的性质是在树上合并两个点集的直径时,新的直径的端点一定来自于原来的四个端点。
有了这个性质就很好做题了,用线段树维护点集合并的过程,黑点的初值为 \((x,x)\) 即自己到自己,白点的初值为 \((0,0)\) ,代表不可作为端点,单点求和,全局查询。
时间复杂度 \(O(n\log^2 n)\),除去线段树的 $\log $,还有一个在合并时算距离 lca 的 \(\log\) ,可以用 \(O(1)\) lca 消去,但没必要。
下午 ZJCPC 耻辱五题下播。
遥远的国度
LCT
可做 ? 其实是简单树剖分讨题,
考虑当前根 \(rt\) 与 \(x\) 相对位置的情况,可分为三种
- \(rt=x\) 直接查全局答案。
- \(lca(rt,x) \neq x\) 对 \(x\) 没有影响,正常查子树。
- \(lca(rt,x)=x\) 只有 \(rt\) 所在的 \(x\) 的儿子的那棵子树不能查,扣掉那段
dfn
序后查剩下的就行了。
9.30
特别行动队
把 \(X\) 拆成 \(s_i-s_j\),就是一个经典的斜率优化式子,直接上李超树就行了。
Sequence
先对于每个 \(a_i\) 减 \(i\),将问题转化为最长不下降序列,限制就变得宽松了。
每个区间想要代价最小,一个显然的想法是取中位数。
我们对于每个数,如果合并之前就满足不下降,就保持不变。否则就与后面的合并来保持不下降的限制。
最后的答案就是中位数加上 \(i\) 后的结果。
小卡与落叶
线段树合并裸题,动态开点线段树的下标记录深度,记录下每个询问时,黄树叶的深度,每次区间求和即可。
10.2
模拟赛,写一下题解
T1
经典 trick ,把大于 \(X\) 的数赋 \(1\),其他赋 \(0\)。区间排序就变成了区间求和和推平。
线段树维护即可。将大于改为大于等于,变化的位置就是 \(X\) 的位置。
T3
发现禁止出现的子串的长度很短,只有 \(6\) ,所以维护字符串的后 \(5\) 位,由于字符集大小只有 \(2\),所以直接状压就行,再用矩阵加速一下,随便跑。
T4
最值分治板子,消去了最值的影响,变成了一个求区间小于/大于某个数的个数,传统二维数点的常数有点大,发现前缀和据有单调性,直接二分就可以了。
10.3
模拟赛,写一下会的题
T1
看到限制次数中有一个 \(\log\) 就要往带 \(\log\) 的算法上想,考虑分治,我们更希望每个元素被操作时位置比较靠后,所以优先给右区间赋值,再给第一个赋值,将整个区间翻转,左区间就到了靠后的位置,分治处理即可,注意区间是否处于翻转状态。求权值的代码照着 check
贺就行了。
10.4
忘情
式子很好化,直接把 \(\overline{x^2}\) 放进括号里,就是 \({((\sum x) + \overline{x})^2}\)
这是一个很版的斜优DP了,再用 wqs 二分消去个数的限制就可以了。
Strange Alice Game
简单数据结构题,先对值域扫描线,每次加入当前扫到的值,再查询前面第 \(k\) 个数,就是从哪里开始刷可以刷这个怪。
接下来就是简单扫描线,把询问挂在右端点,每次查询有多少个其实位置,小于等于左端点就行了。
时间复杂度 \(O(n\log n)\) 。
动态DP
用广义矩阵乘法,把转移式写成只和重儿子有关的形式,剩下的写进矩阵,每次修改时,修改这个点向上的每条重链即可,查询时查询根所在重链。
可以每条重链都开一颗线段树,常数更小,可以卡过加强版。
10.8
刚回来就考试。
T1
首先显然每只变色龙都至少要吃一个球。
注意到如果将一只变色龙吃下的所有球同时反转颜色,这只变色龙的最终颜色也会跟着反转。所以每只变色龙最后是红色的概率都是 \(\frac{1}{2}\),答案即为总方案数除以 \(2^k\),算总方案数只需简单容斥即可。
T2
要让没有重点的最短链最长,当固定根时,让每条链都从叶子开始向上延申,有冲突就让最短的先延申,这样的策略是最优的,答案就是到根的那条链或某个子树内的次短链。
维护 \(f_u\) 表示 \(u\) 子树内所有链的长度。
\(S\) 为所有次短链的长度。
换根时更新两个集合即可。
T3
中间忘了
T4
经典 \(trick\) 挂虚点求直径,对每个 \(u\) 建一个虚点,相连的边权为第一颗树上 \(u\) 到根的权值和,因为直径一定在虚点上,所以可以直接看作加了一个点权,用线段树维护出树的直径,离某个点最远的点一定在直径端点上。
换根时,用树状数组做一个子树加就可以了。
10.9
哈希真可爱
哈希求回文
正反串分别哈希一下,比较对应段哈希即可。
哈希求SA
快速排序,cmp
里写一个二分哈希求 lcp
,直接比较 lcp
的后一位即可。
时间复杂度 \(O(n\log ^2 n)\)
哈希求循环节
质因数分解
Hash killer
挑战 npc,勇夺图灵奖。
10.10
模拟赛
CF1830E(*3500)
这里有一堆结论,最后 操作数等于
动态逆序对维护一下就好了。
CF1004F(*2600)
单次询问是可以双指针直接做的,有修改,就把双指针搬到 push_up
里,然后经典结论,答案只会改变 \(\log\) 次,存一下转移点即可。
CF1572D(*2800)
显然是一个二分图,两个点如果有边,边权为两者权值之和,费用流跑最大权匹配就行了,发现时间复杂度错完了,因为只取 \(k\) 次,所以 只用至多 \(k\times 2n\) 条边就够了,取权值大的那些边,直接mcmf
就行了。
CF997E(*3000)
不用吉司机的做法。
这个东西一看就很序列分治,我们挂完询问后考虑答案的贡献形式。
类似于连号区间数,当 \(max,min\) 在同一边时,可以直接计算出满足要求位置,判断是否合法即可。
否则就要给 满足类似 \(max+l=min+r\) 的位置加上贡献。
普通的线段树很好维护第一种形式,却不怎么容易维护第二种。
再次深挖性质,发现 \(r-l\le max-min\),也就是说等式成立时 \(min+r\) 应取最大值,所以在线段树里再记录一下区间最大值和最值个数,就能实现给区间内的最大值加上贡献。
我们就可以完成本题的贡献计算。
回答询问就是简单区间求和。
具体地讲,对于计算跨 \(mid\) 的贡献。
我们枚举一个 \(i\),用双指针维护 \(j\) ,代表 满足\([i,j]\) 的 \(max,min\) 都在 \([i,mid]\) 的最小右端点,\(k\) 代表 \(max\) 在 \([i,mid]\) 的最大右端点。
我们分别对 \([mid+1,j]\) , \((j,k]\) 采用上述的贡献计算方法,用线段树维护每个位置的贡献,遇到询问就查询询问区间内的贡献和。
挂询问类似于 [NOIP2022] 比赛 和 序列。