2023.01.25 题目选讲 学习笔记
讲课人:罗恺。
这讲都是好题,只可惜有的题我感觉很难用文字表达清楚做法,把人讲懂,比如 两题。之后再接再厉!
1.CF1361E James and the Chase
首先一个点是好的当且仅当以这个点为根搜出一棵 dfs 树,dfs 树中只有树边和返祖边。由后者推前者是容易的,由前者推后者只需从反证法的角度考虑,发现不可能有前向边或横叉边。
于是我们可以先随 个点并判断它们是不是好点,如果都不是我们可以认为好点数没有 ,否则我们找到了一个好点。
以我们找到的好点为根搜出一棵 dfs 树,现在尝试判断每个点是否是好点。对于点 ( 不为根),首先 到子树内的点都恰有一条路径,其次 子树内指向子树外的边至少有一条(因为强连通),如果 子树内指向子树外的边有至少两条,那么 一定存在至少两条简单路径到 的父亲;否则, 子树内指向子树外的边恰有一条,设指向了 。
接下来我们说明 到每个点的简单路径数和 到每个点的简单路径数相同,从而 是好的当且仅当 是好的。首先 到 子树内每个点的路径都恰有一条;其次如果 不是根,则 到子树外的点一定要经过 子树内的某条返祖边,并且该路径一定不经过 子树,原因是 子树内没有能到 子树外的返祖边。所以按深度从小到大更新即可。
设随机一个点并判断其是否是好的的次数为 ,则时间复杂度 ,空间复杂度 。
2.Gym104090E Oscar is All You Need
首先 时只有两种可能的结果。不妨设 。
首先 可以在两步操作内被换到位置 。现在假设 已经分别被换到前 个位置了,我们尝试把 换到位置 。
此时序列一定形如:。其中 , 含有若干个数(可能为空)。
如果 ,那就做完了。
如果 ,那么可以 。
如果 ,将 分成 两部分且两部分都不为空,那么可以 。
剩下 的情况,此时序列前 个数一定都归位了。注意到上述过程每将一个数移动到对应位置,花费次数不超过 ,因此该部分不超过 步。
此时,只剩下 和 两种情况,前者已经做完了,后者我们把 划分成 两部分且两部分都不为空,那么可以 ,花费 步。总步数不超过 。
时间复杂度 ,可以用 splay 优化到 ,空间复杂度 。
3.CF843D Dynamic Shortest Path
首先第一次求最短路就直接用堆优化的 dijkstra 求。求完之后设 到 的最短路长度为 ,那么对于一条边 我们把它的边权 做一个调整得到一张新图:。这个调整的作用是,如果一条边在最短路图上那它的边权为 ,否则边权一定大于 。同时,新图上 到 的任何一条路径都比原图上对应路径的长度大了 ,所以新图上求出的最短路即为原图求出的最短路。有了这个调整后我们每进行一次 操作就把新图建出来并求出 号点到每个点的最短路。注意到新图上 到每个点最短路长度都不超过 ,因此我们就可以用 个队列来代替 priority_queue,从而在 时间内求出新的最短路(即 的 dijkstra, 是源点到每个点最短路的最大值)。
时间复杂度 ,空间复杂度 。
4.UOJ#703. 赵云八卦阵
首先发现 是可以异或上它前面的任意一个数 的,只要依次操作 。所以第 个数的取值范围为 的线性基能表示出的数异或上 得到的集合,记该集合为 。
我们从前往后求出每个前缀的线性基。如果 已经能被 前缀的线性基表示出来了,那么 就是 前缀的线性基能表示出的数的集合,此时对于任意 都有 。那么如果有一种最优解中第 个位置不在 LIS 中但是它前面有数在 LIS 中,我们可以把第 个位置上的数变成它前面最后一个在 LIS 中的数,把前面最后一个在 LIS 中的数从 LIS 中删去并把第 个位置加入 LIS 中,得到的还是最优解。这样,对于每个 能被 前缀的线性基表示出来的位置 ,要么位置 在 LIS 中,要么前 个位置没有数在 LIS 中。
所以,存在一种最优解,满足 能被 前缀的线性基表示出来的位置选的是一段后缀。那么我们就可以把 能被 前缀的线性基表示出来的位置依据它们的 集合分成若干个连续段,满足每个连续段的 完全相同,不同连续段的 不同,可以发现一共有 个连续段。同时, 不能被 前缀的线性基表示出来的位置也有 个。
设 不能被 前缀的线性基表示出来的位置有 个,依次为 。从后往前 dp,设 表示 中所有 能被 前缀的线性基表示出来的位置都选, 中 不能被 前缀的线性基表示出来的位置选了 个,此时 LIS 开头的最大值。从 转移到 时首先要在 这些位置的 中定位出小于某个数的最大数,接下来贪心能选就选,有两种可能,一种是贪心地选的过程中直接把 中剩余能选的元素都选完了,此时直接更新答案;另一种可能是没有选完,此时再枚举 是否选,并在对应的线性基中定位出小于某个数的最大数即可。
状态数是 的,每次在线性基中定位是 的,从单个状态转移到之后的状态要定位 次,所以该部分复杂度是 的。
总时间复杂度 ,空间复杂度 。
5.Gym104090B Useful Algorithm
我们对每个 求答案,实际上就是求 。令 (如果集合为空则 ),也就是求 。令 ,变成了求 。 可以用 multiset 维护,所求式子可以用线段树维护。
时间复杂度 ,空间复杂度 。
6.Gym104076I Shortest Path
钦定一条边,表示它是我们经过的路径上长度最小的边。不妨设这条边是 ,由于我们经过了它所以我们走的路线一定形如 到 的某个端点,经过 并从另一个端点离开。在 到 的某个端点的路上,如果有 外的点 在路线中被经过了至少 次,那么第一次到 后走了一个环又回到了 ,第二次到 后走了一个环又回到了 ,这两个环中如果有偶环可以把偶环删掉,并替换成等量边数的最小边;如果都是奇环可以把两个环都删掉,并替换成等量边数的最小边,此时路径不会变长。所以在 到 的某个端点的路上经过 外的每个点的次数都不超过 ,同理从 另一个端点离开到 经过 外的每个点的次数也都不超过 。也就是说,在所有经过 条边的从 出发到 的路径外,除了 外的点都经过不超过 次,那么剩下的次数一定是在 这条边上来回走。
因此在经过的路径边数足够大时,我们一定是从 出发走到一条边 的一个端点,在 上来回走,再从 的某个端点出发走到 。同时注意到按照上述流程,真实答案一定会被算上,且如果钦定的边不是路径上最小的边答案并不会更优(因为也是一种合法的走法)。
我们先求出从 和 出发经过 条边到每个点的最短路。接下来枚举一条边 ,枚举从 走到 的哪个端点以及走过的路径边数 ,设此时最短路径长度 ,枚举从 的哪个端点离开走到 以及走过的路径边数 ,设此时最短路径长度 ,设总共经过 条边,那么在 上来回走的次数就是 ,设 长度为 ,则路径长度为 ,
显然要选 最小和 最小的两条路径。同时注意到路径长度的形式可以表示成 的形式,不难想到可以用凸包来维护。
时间复杂度 ,空间复杂度 。
7.CF833E Caramel Clouds
我们尝试对每个位置 求出 表示 内最长可能的未被覆盖的长度,这样询问就可以通过二分快速回答。进一步地,我们发现只需要求出 这些位置的 值即可。
离散化后,设第 个位置对应的原坐标为 。假设我们已经求出了 ,如果 没被覆盖,那么 。如果 被覆盖了至少 次,那么 。
如果 被覆盖了 次,一种情况是 最终被覆盖,;另一种情况是 最终未被覆盖,,其中 表示只考虑右端点(离散化前)不超过 的区间,未被覆盖的长度有多长。
如果 被覆盖了 次,一种情况是 最终被覆盖,;另一种情况是 最终未被覆盖,此时要求的东西形如,在右端点不超过 的区间中,去掉某个区间后,新增的未被覆盖的长度,也即只被这个区间覆盖的长度。记当前右端点不超过 的区间中,只被区间 覆盖的长度为 ,那么 还可以为 (前提是唯一覆盖 的区间花费小等于 )或 (前提是唯一覆盖 的区间花费和区间 的花费之和小等于 ,且区间 的右端点不超过 )。
所以我们需要动态维护出每个时刻 ,只考虑右端点不超过 的区间时的 和 。注意到 只在 增加或有位置被覆盖的次数变化为 时才会变化, 只在有位置被覆盖的次数变化为 或 时才会变化,所以 和 的总变化次数都是 的,可以用珂朵莉树维护。
动态维护出 后,我们还要在一个时刻查询 不超过某个值的 的最大值,这可以用单点修改,前缀最大值线段树维护。
时间复杂度 ,空间复杂度 。
8.Gym104076B Torch
称走在前面的人为 A,后面的人为 B。设不考虑 B 会被 A 挡住的情况下, 时刻 A 的位置为 ,B 的位置为 ,则实际上 时刻 B 的位置为 。 是可以 计算的,因此我们只需快速计算 。
注意到可能成为 最大的时刻 一定满足下述条件之一:,或 时 A 的灯暗且 时 A 的灯亮,或 时 B 的灯亮且 时 B 的灯暗。但这还不够,我们发现还可以把 的情况转成 的情况,那么所有可能成为 最大的时刻 只有 种了。所以把询问的时间(要先转成 )和可能成为 最大的时刻 一起排序,就做完了。
时间复杂度 或 (取决于排序),空间复杂度 。
9.Gym104076J Skills
注意到开始学一个技能后则这个技能的熟练度不会被减到 及以下,不然不如把之前学习该技能的时间拿去学其它技能,所以在开始学一个技能后我们可以忽略熟练度和 取 的条件。
我们还发现,开始学一个技能后,中断的时间不会太长。具体来说,不会超过 天。比如说我们找到一个技能,它是所有技能中最早中断了 天的,那我们在其第一次中断了至少 天的这段中断期间中的第 天放弃学习别的技能,转而学习这个技能,此时损失是原本要学习的技能能获得的熟练度和那个技能中断的天数,收益至少是接下来 天每天少损失 熟练度。损失中前者是 的,对于后者由于我们找到的技能是最早中断了 天的,所以此时原本要学习的技能中断的天数不超过 ,因此调整完会更优。
于是就可以 dp 了,记一下当前的天数,这天学习的技能,以及另外两个技能上次学习的时间或者还未开始学,状态数是 的,转移是 的。
总时间复杂度 ,空间复杂度 。
10.CF1616G Just Add an Edge
先特判掉 到 本来就有哈密顿路径的情况。
其余情况一定会经过新加的边 。为了统一起点和终点,我们添加两个点 , 向所有点都连边,所有点都向 连边。于是最终路径一定形如从 走编号升序且连续的点到 ,从 走编号升序的点到 ,从 走到 ,从 走编号升序的点到 ,从 走编号升序且连续的点到 。
我们考虑把每个点染色:如果走到该点时还未走过新加的边,就染成 ,否则染成 。我们把 从左往右从小往大排成一个序列,于是整个序列中最靠右的 就是 ,最靠左的 就是 。一种染色方案是合法的,当且仅当每个点有恰好一种颜色,且同种颜色的点按编号升序排序后,编号相邻的点在图中都有边相连。
首先枚举最靠左的 (即 )的位置。从小往大考虑每个数染成什么颜色,可以得到一个比较显然的 dp:设 表示目前染成 的点中最大的是 ,染成 的点中最大的是 ,此时是否有合法的染色方案。转移枚举下一个点的颜色。总时间复杂度是 的。
一个优化是,颜色相同的段我们可以合并转移,具体而言,判断 能否都染成某种颜色,只需判断 这些边是否都存在即可。也就是说,在上述的 dp 中,我们只用考虑形如 和 这些状态。于是可以重新设 表示考虑了前 个点的颜色, 和 的颜色不同, 的颜色为 ,此时是否有合法的染色方案。时间复杂度降为 。
由于此时图中没有哈密顿路径,所以一定可以找到一个最小的 满足 的边不存在,那么此时 和 的颜色就一定不相同!所以我们先得到一个结论,。
注意到我们对 有个限制 ,实际上我们 dp 的过程中可能出现 的情况只有 ,而这种情况能使图产生哈密顿路径当且仅当 中只有 不存在。这是很好特判的,即如果有这种情况,我们先把答案 。于是 dp 时就没有 的限制了!
再来看看 dp,我们是先枚举了 ,再 dp 求答案。但是实际上,对于不同的 ,转移都是一样的,唯一不同的只有初值,我们必须减少无用的重复转移。
这就又要用到我们前面说的「 和 的颜色一定不相同」了,可以发现, 不可能一步转移到 。这启发我们可以不枚举 ,因为 都要先转移到 ,再通过 转移到 。所以 和 其实是相对独立的,我们只关心 能使 变成什么样,和在只给定的 的情况下 能变成什么样。
后者可以直接枚举所有可能的 初值,用上述 dp 求出最后 的形态。前者我们可以从后往前求出 表示如果一开始只有 ,能否让 ,这也可以 求。最后把两边 dp 的信息合并一下就好了。
时间复杂度 ,空间复杂度 。
11.CF643G Choosing Ads
如果 就是要求区间严格众数,可以用线段树维护摩尔投票的方法做。具体的,线段树上每个节点维护对应区间摩尔投票后留下的数,以及它的次数,那么合并两个节点可以 合并。
拓展到 的情况,我们时刻维护不超过 个数,表示摩尔投票筛出来的数,每加入一个数 ,如果 出现在了这些数中,则将 的次数 ;否则,如果当前维护的数个数小于 ,则把 加进维护的数中并将 的次数设为 ;否则,将这 个数的次数全部 ,并把次数变为 的数从我们维护的数中删掉。接下来我们说明为什么出现次数至少占数的总数的 的数一定会出现在我们最终维护的数中。
考虑什么时候我们维护的数的次数会减少,只有当我们维护的数恰有 个,且加入一个不同于它们的数时才会发生。那么我们从某种数 的角度看,如果 某次加入时 已经存在或维护的数个数小于 ,则 的次数会 ;如果 某次加入时维护的数恰有 个,则 的次数不会 ,但是可以认为是 「抵消」掉了 个不同于 的数;如果加入某个不同于 的数 时维护的数恰有 个,且 在其中,则 的次数会 ,但是也可以认为是 「抵消」掉了 个不同于 的数。假设数的总数为 , 出现了 次,那么不同于 的数有 个, 最多会「抵消」 次,故 最终的次数至少为 ,下面我们说明 。
由于 ,只需说明 ,即 。
。
所以至少占数的总数的 的数一定会出现在我们最终维护的数中。
上述过程还是可以用线段树维护,只不过合并的复杂度变成了 。
总时间复杂度 ,空间复杂度 。
顺带一提,如果不允许输出询问的答案可以包含错的数或重复输出,那就变成了 CF840D Destiny 这题,求出可能答案后用主席树算一下真实出现次数即可。
12.UOJ#749. 【UNR #6】稳健型选手
称先手为 A,后手为 B。先考虑单组询问怎么做。对于每个长度为 的前缀,B 至少会选 个元素,并且对于满足该条件的所有选法,A 每次选他要选的编号最小的元素,最终一定能选到他想要的所有数。于是可以反悔贪心。
同时由上述条件知道如果区间长度是奇数,那么最后一个数一定是归 A 的,所以我们只用考虑区间长度为偶数的情况。
假设我们已经用堆维护出了只看长度为偶数的区间 中的数,A 选的数的集合和 A 不选的数的集合。如果往区间末尾加两个数,区间内原先不选的一定不选,原先选的可能不选,那么根据贪心我们应该把这两个数加入 A 选的数的堆,并把 A 选的数的堆中最小的数移到 A 不选的数的堆中。如果往区间开头加两个数,区间内原先选的一定选,原先不选的可能选,那么根据贪心我们应该把这两个数加入 A 不选的数的堆,并把 A 不选的数的堆中最大的数移到 A 选的数的堆中。这个过程如果用不删莫队实现可以做到 。
实际上,我们可以把上述做法拓展一下。考虑分治,假设当前分治区间为 ,令 ,根据上述做法可以发现 的任意两个长度差为 的后缀,选的数至多有 个不同,所以我们可以用主席树维护 的每个后缀选了哪些数,同理也可以维护 的每个前缀选了哪些数。然后考虑回答所有 的询问,我们已知 中选的数和不选的数的集合,那么合并在一起后, 中不选的数一定仍不选,选的数可能变不选, 中选的数一定仍选,不选的数可能变选。同时可以发现, 中新的不选的数一定是之前选的数中最小的几个, 中新的选的数一定是之前不选的数中最大的几个,于是可以主席树上二分。
时间复杂度 ,空间复杂度 。
13.CF1764G3 Doremy's Perfect DS Class (Hard Version)
设询问 得到的结果为 。
如果 是奇数,设当前已经确定 在 中,令 ,询问 和 ,考虑 ,显然除以 下取整结果相等的只有 和 , 和 ,……, 和 这些对。讨论可知,如果 在 中,则 ,否则 ,于是可以根据 的值在 次内二分出 的位置。
如果 是偶数,我们仿照 是奇数的情况做,发现除以 下取整结果相等的对有 和 , 和 ,……, 和 ,而 和 一样是独立的,没有数和它配对(除以 下取整相等)。同样询问 和 ,讨论可知,我们可以根据 的值,判断出三种情况:1. 都在 ;2. 都在 ;3. 一个在 ,一个在 。前两种情况直接得到了 的位置,但是第三种还没法区分哪个是 哪个是 。于是对于第三种情况我们再问一次 (或者 )便可知道 和 谁在前面了。注意到我们只需要求一次 的相对前后关系,所以这么做最坏要询问 次。
考虑把多出来的 次优化掉。当 是偶数且 时,我们原本要花 次才能询问出 在哪,现在我们直接讨论。如果此时还没确定 的相对前后关系,那么 必定都在 中,那么询问 或者 可以确定出哪个是 哪个是 ,最坏花费 次。如果已经确定了 的相对前后关系,那么 中的 个数一定是一个 和一个 中的数 ,注意到此时我们已经知道了 ,如果和 配对的数在 中,那么我们询问 看 是否等于 便可知道 的位置;同理,如果和 配对的数在 中,那么询问 看 是否等于 即可。最坏询问次数 次。
14.UOJ#708. 【UER #10】磁球与磁棍
一个连通块合法,当且仅当连通块内所有叶子到块内深度最浅的点的距离都是偶数,或者连通块内所有叶子到块内深度最浅的点的距离都是奇数且深度最浅的点不是叶子。
不难想到树形 dp。设 表示 子树内,划分成了 个连通块, 所在的连通块中所有叶子到 的距离模 都为 , 到父亲的边是否有断掉,此时是否有合法划分方案。时间复杂度是 的。
我们称一个点为自由点,当且仅当它到连通块内所有叶子的距离都是奇数。
可以发现,我们划分出的连通块要么大小为 ,要么大小至少为 ,后者中一定有自由点。
对于一个 度的自由点,我们可以将其两条出边都断掉,连通块数相比之前增加 。
对于一个至少 度的自由点,我们可以将其任意两条出边断掉,连通块数相比之前增加 。
对于一个 度的自由点,我们可以将其任意一条出边断掉,连通块数相比之前增加 。
如果有至少两个 度的自由点,那么我们任选两个 度点各断一条出边,连通块数相比之前增加 。
也就是说,只要有自由点,并且自由点不是一个 度点,那么就存在方案使得连通块数相比之前增加 。
注意 时有解, 时无解, 时合法划分方案中一定有自由点。自由点只有一个 度点的情况,当且仅当 时可能出现。
也就是说, 且 时,如果存在划分方案有 个连通块,则也存在划分方案有 个连通块。
现在我们只关心已知连通块数的奇偶,最少能划分出几个连通块。设 表示 子树内,划分成了偶数/奇数个连通块, 所在的连通块中所有叶子到 的距离模 都为 , 到父亲的边是否有断掉,此时能划分出的最少连通块数。
时间复杂度 ,空间复杂度 。
15.CF1007E Mini Metro
我们先来理清一下题目的时间线:对于自然数 ,在 这些时间里发生的事有, 时刻来车接人, 时刻站台来人。
首先发现有车接第 站的人时,前 站的人都已被接完。其次可以在最后补一个站 ,,这样每辆车都会满载。
设 ,,。
设 表示只考虑前 站,初始第 站有 个人,存活到 时刻,每辆车都必须是满载的(只能载前 站的人),需要的最少车数(如果无解则为 )。
设 表示只考虑前 站,初始第 站有 个人,存活到 时刻,每辆车都必须是满载的(只能载前 站的人),且 时刻来完车后前 站的人都已被接完,需要的最少车数(如果无解则为 )。
答案就是 。下面考虑怎么转移。
情况 : 时刻前没有车从站台 接走人。
此时需满足 ,即在 时刻第 站的人数不会超限。并且,前 站能独立存活到 时刻,即 。如果上述条件都满足,便有转移:
令 ,这是清空前 站至少要的车数。如果 ,就有车没满载没法更新 了。否则,现在至少要用 辆车,事实上,用 辆车是能清空前 站的,原因是 时刻前没有车从站台 接走人,那么在 时刻把剩余的车来了即可。
情况 : 时刻前有车从站台 接走人。枚举时刻 ,表示上次从站台 接走人是在 时刻。
这种情况的流程如下:
-
前 站独立存活 时刻,并在 时刻来车让前 站全空。
-
如果在 时刻来车后第 站剩余人数加 比 大,即如果 时间都不来车的话, 站台的人数会超限,则在 时刻继续来车直到 时间都不来车 站台的人数也不会超限为止。注意要判断车能否满载。
-
从 时刻来完车开始到 时刻为止来的车都不从站台 接走人。我们要保证在这段时间中前 站能独立存活。
-
存活到了 时刻,就可以直接更新 了。对于 ,我们可能要再来一些车使前面的站都清空。此时算出 的下界,类似情况 分析可得下界依然能取到。
这便是大致的流程了。如果你完全理解了情况 ,那么情况 根据上述流程也不难推出(至少我理解流程后已经能写出代码了)。最后情况 转移的式子长成这样(式子没判转移是否合法):
令 ,,。
时间复杂度 ,空间复杂度 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端