十二月份训练手记
12.1 - 1 - Problem - E - Codeforces
思路
想了一会关于区间的方向未果,意识到一件事情,就是固定区间的一端,则区间 set (其他的什么也可以,这个方便一点
收获 & 反思
简单
12.1 - 2 - Problem - F - Codeforces
思路
首先不难发现对于每一个位置编号比自身数值来得大的元素,一定需要一次
然后我们考虑构造到整个下界,想法也很简短,对于置换上的每一个置换环,考虑环上的每一段连续的 “向后跳” 的边(对应着一串可以被连续转移的位置编号比自身数值来得小的元素) 对应着至少一个 “向前跳” 的边,也就是我可以按置换环中的最小元素排列后,这样处理每一个置换环:
- 从起点最小的一段连续的 “向后跳” 的边出发,沿着边运送数字,直到遇见第一个向前跳的边的起点,带着这个元素回到起点再送到对应的位置
- 此接下去重复第一部分直到整个置换环被完全 “修正
此时指针应该在环的最小节点处并且缓冲区为空,所以我们可以不用返回直接寻找下一个置换环继续操作” ,
这样就证明了答案的充要性,维护答案也很简单,注意到每个元素做贡献至多三个区间,打上差分标记后前缀和统计即可
至于反转操作就更没意思了,对反序列也求一遍答案并实时维护对应的反序列的状态即可
收获 & 反思
充要性的证明,还蛮好玩的,算半个构造题吧
12.2 - 1 - Problem - D2 - Codeforces
思路
很好考虑,也很好玩
我们发现
首先是对于
然后不难注意到,当
那最后就来到了比较神秘的
收获 & 反思
很喵喵的题目,思维比较灵活,好题
12.3 - 1 - Problem - B - Codeforces
思路
我们考虑固定手上的彩票号码之后,怎么去求让我们能中奖的号码有多少?
我们不妨假设我们的号码
同理可以求出右侧的端点,从而计算出总共可能的中奖号码数量
由上述的计算方式我们不难发现,当
最后注意特殊情况,就是如果所有情况都可以中奖,那么答案就是
收获 & 反思
感觉思路不是特别难,但是为什么想不到呢?可能是因为没有手玩?
12.4 - 1 - Problem - E - Codeforces
思路
注意到不寻常的
因为显而易见的
收获 & 反思
需要一点注意力,我的注意力虽然不多,但够用
12.5 - 1 - Problem - F - Codeforces
思路
我们知道一个经典的反悔贪心问题是 “给定权值总和上限,求最多选取物品数
就是这样,做完了...... 吗?
傻逼出题人全家活暗暗卡你吗的 multiset 的常数
换成 priority_queue 就过了
收获 & 反思
没什么好说的,经典反悔贪心 + 经典二分答案
哦,priority_queue 常数小
12.6 - 1 - Problem - F - Codeforces
神秘喵喵题
思路
我们发现这题比较神秘的地方在于他有一个 “差的绝对值
更深入地思考一下会得出一个结论,就是黑色节点一定形成一个连通块,证明很简单:
- 若不是一个连通块,那么一定存在一条边连接一个黑点和一个白点,并且断开之后黑点所属的连通块包含更少数量的黑点,此时将这条边两端的节点颜色调换,答案一定更优
显然在这个连通块以外的边贡献固定为
但是如果枚举重心出错怎么办?不难发现此时的答案一定不优,所以并不影响最终答案的求取
收获 & 反思
重心的神秘性质,好玩的勒
等价定义:
在树中删去结点
后,得到的图 中每个连通分量的大小均不超过原树结点数的一半。 在所有删去某个结点后得到的最大连通分量大小中,删去结点
时所得到的值最小。 树中所有结点到某个结点的距离和中,到结点
的距离和最小。
树的重心如果不唯一,则恰有两个。这两个重心相邻。而且,删去它们的连边后,树将变为两个大小相同的连通分量。
在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
把两棵树通过一条边相连得到一棵新的树,那么新树的重心在连接原来两棵树的重心的路径上。
一棵有根树的重心一定在根结点所在的重链上。一棵树的重心一定是该树根结点重子结点对应子树的重心的祖先。
12.7 - 1 - Problem - F - Codeforces
思路
我们很容易就能想到用 dp 来做这个东西,但是我们发现一件事情:dp 需要三维:横坐标,纵坐标,时间,这样看的话好像一定会超时
但是我们可以注意到一件事情:既然角色只能向右下方运动或者停止,那么角色到某一个坐标处的时间和他这一路上的停止次数是可以相互求出的,又不难发现角色至多只会停下来规避轨道炮至多
收获 & 反思
在 dp 的时候,设状态是很重要的,考虑尽可能多的利用已知的信息去简化我们的状态描述,减少总状态数
12.8 - 1 - Problem - F - Codeforces
思路
考虑对于每个数有四种状态:除 + 减,除,减,什么都不干。
引理 1:操作全用完一定不劣。
引理 2:对于除 + 减的数,一定先除后减。
接下来将所有数从大到小排列,得到
显然除 + 减的数一定是一段前缀。直接枚举前缀
为了方便,令
剩下了
- 引理 3:除了
,进行操作的数字必然是 。
对于
假设已经给两段分别配好了各自除和减的数量,那么确定具体的对每个数字的操作呢?
对于不小于
的 : - 减操作的贡献一定是
,而除操作对于 的贡献是 。 - 既然减给谁都无所谓,那么显然要给大的数据,给小的数减。
即对于
的操作是:// ---。 - 减操作的贡献一定是
对于小于
的 : - 对于
,减操作的贡献是 ,除操作的贡献是 。显然,我们应当给大的数据,给小的数除。
即对于
的操作是:--- //。 - 对于
发现了什么?
所以我们不用真的对
时间复杂度
收获 & 反思
很好玩的贪心,好像还有 wqs 二分的做法,后面再说吧
12.9 - 1 - Problem - F2 - Codeforces
思路
首先需要明确一个性质,就是对于带有可重元素的序列,完全排序所需的步骤数依旧是元素数量减去能构造出来的环的数量的最大值,而显然若构造出来的环中存在两个相同的元素,则可以断成两个环,由此可知我们能最大化的交换次数等于元素总数减去数量最多的元素的个数,也就是恰好每个环中都含有一个数量最多的元素
换而言之,我们要检查一个序列是否是最大化交换次数的,就可以去找是否有一个环不包含数量最多的元素,如果能找到,说明可以构造更多环,也就是需要的交换次数并非最大值
接下来就是找环,直接优化建图(采用对应数值的虚拟节点作为边的中转站)然后在图上找环即可,对于有向图找环,一个简单的方法就是拓扑排序,不断删去入度为
收获 & 反思
核心出装:
- 排列上环的数量与交换次数的关系
- 中继节点优化建图
- 拓扑排序判环
12.10 - 1 - Problem - E - Codeforces
思路
考虑作为 “二者选其一” 的经典模型来做,先假设最开始全都变成
而对于每个人要求的组合,如果要求为
这里关于总收益减去最小割的推导:
原先的收益是所有要求为
的 ,减去要求为 的赔款 ,再减去所有初始为 的点的改造费用,也就是: 考虑我们收益的变化量是所有连向源点的边的权值之和减去最小割,也就是:
也就是我们最后的总收益为:
收获 & 反思
经典的网络流建模思路,多练,别到时候犯傻了板子题不会做
12.10 - 2 - Problem - G - Codeforces
思路
不难发现钦定一个根节点之后,一次操作相当于把所选的节点深度
考虑证明充分性,就是在按照深度从大到小来操作每一个节点,就可以达到要求(容易证明
收获 & 反思
没什么好说的,简单结论题
12.11 - 1 - Problem - C - Codeforces
思路
既然它保证了两个多项式是相似的,那么我们只要找一个必要条件就可以了
原本的多项式次数太高了,我们考虑给他求导降次,但是我们又不知道他的表达式,于是我们可以把原本的多项式看作一个下降幂多项式,然后对他做等间距(间距为
于是我们就可以知道求他的
问题就在于我们当然不会求出所有的
于是就只需要预处理组合数就行了
收获 & 反思
一些非常色情的知识点,值得反复学习:有限微积分与数列求和 - 洛谷专栏
12.12 - 1 - Problem - E - Codeforces
思路
一件显而易见的贪心思想就是,如果一个人需要的某一种食物,它的数量大于等于需要它的人数,那么这个人的需求一定可以被满足,不会和其他人产生可能的竞争,所以我们贪心地把他放到最后一个
我们接着这个思路,考虑证明这是存在答案的充要条件:
- 首先考虑充分性:若对于每一步我们都能找出这样的人放到序列的最后,那么显然可以构造出一个合法的序列
- 然后考虑必要性:考虑反证,因为前面的操作是不具备后效性的,所以如果到了某一步为止,场上所有的食物的需求都大于储备,那么不妨考虑任何一个人进厨房,除非某种食物的余量为
,否则这个人需要的两种食物的储备和需要他们的人数都会减少一,也就是场面依然保持 “场上所有的食物的需求都大于储备 考虑操作到只剩一个人,那么由于” , ,所以这个人一定没东西吃,必要性证明完毕
于是我们只需要统计每个食物的需求量和储存量,然后不断取出 “储量安全” 的食物的需求者,把他们放进末尾并且维护对应食物的需求人数即可
收获 & 反思
经典的贪心 + 证明充要条件,没什么好说的,做不出来埃及拔草
12.12 - 2 - Problem - 833B - Codeforces
思路
不难意识到这个
我们不难发现其复杂度瓶颈在于我们每次更新都需要比较 dp ,具体地,我们初始时用前一层的
收获 & 反思
考虑数据结构优化 dp ,不只是简单的查询,也可以是以便查询一边有修改
12.13 - 1 - P1361 小 M 的作物
思路
简单的网络流 “二者选其一模型
12.13 - 2 - P2057 [SHOI2007] 善意的投票 / [JLOI2010] 冠军调查
思路
简单的网络流 “二者选其一模型
12.13 - 3 - P4313 文理分科
思路
简单的网络流 “二者选其一模型
12.13 - 4 - P1646 [国家集训队] happiness - 洛谷
思路
简单的网络流 “二者选其一模型
12.13 - 5 - Problem - 1975F - Codeforces
非常厉害题
思路
真的是神仙题,不知道怎么出出来的,不知道怎么想出来的
大概题意就是给定一系列二进制数
直接来做的话,不难想到枚举
这就是本题最厉害的地方了,对于一个要求
我们考虑集合
如果最后一位是
,那么他始终不做贡献,也就是对于 来说,对 的要求和对 的要求是一样的,所以可以合并为一个要求 如果最后一位是
,那么他只在 的相应位为 时有 点贡献,也就是对于 来说,对 的要求恰好比对 的要求少一的,所以也可以合并为一个要求
至此一个规模为为
具体地,我们每次将
收获 & 反思
真的超级厉害题,对于这种 “要求” 类型的题目,可以考虑往归纳要求,合并要求,收敛子问题这样的方向去思考
12.14 - 1 - Problem - 2128F - Codeforces
非常厉害最短路
思路
我们考虑先证明如下引理
- 如果存在方案,那么将方案中对应的最短路全部取下界,其他边全部取上界一定是一个合法的构造
- 因为增加不在路上的边长不会改变最短路,也不会使任何经过
的路径缩短,一定不劣 - 同时,减少在路上的边长至多只会使任何经过
的路径减少同样的长度,路径长短关系不变,一定不劣
- 因为增加不在路上的边长不会改变最短路,也不会使任何经过
- 那既然能这样构造,那么题意等价于存在一条路径,按照上边的方法构造之后,使得你从路径上任意一点出发去
在回到路径上任意一点都要比直接沿着路径走来得慢 - 充分性显然,若能构造则一定合法
- 必要性,如果对于任意一条路径都不存在,说明任何上述构造都不合法,又因为若有合法路径一定能由上述构造构造出来,所以若不存在则没有合法方案
- 最后就是最牛逼的部分了,我们对问题做非常厉害的改写:
- 考虑你是一名通缉犯,沿着长度为下界的边移动,你每走到一个节点处,这里的人就会尝试去警察局
点报警,此后警察会出警来抓你,你需要考虑能否有一条路使得你能够不被抓到从 到达 - 对于这个问题,我们显然是可以用 Dijkstra 来维护求出的:我们令
表示到某个点的时候警察最短已经出警的时间,如果还未出警就为负值,表示即将出警,转移就是从前一个结点出警的警察和新的报警人之间取最值,若到某个点处 大于等于 点沿上界边到该点的最短路,说明在此点被捕无法避免,则无法用这个点继续松弛
- 考虑你是一名通缉犯,沿着长度为下界的边移动,你每走到一个节点处,这里的人就会尝试去警察局
至此问题得解
收获 & 反思
非常厉害题,都不知道怎么想出来的
就直接改写了?就直接改写了?
12.15 - 1 - Problem - 601E - Codeforces
思路
经典的线段树分治,用暴力维护添加元素来代替 “删除” 操作
但是在这道题当中,背包 dp 用到的数组不支持回退操作,直接给每个节点开一个的话又会爆空间,于是我们隆重介绍 —— 按深度优化空间存储
不难意识到,到了子节点的时候,我们只需要从父结点继承上一个状态,再将自身的节点信息维护进去就可以,而这个过程中有意义的数组数量始终不超过树的深度,也就是我们只要为每个节点保留其上一层的数组信息,就可以当作 “副本” 或者 “回溯
收获 & 反思
嘛,就是这样
12.16 - 1 - Problem - 1819D - Codeforces
动态规划好题
思路
我们考虑最后的答案来自哪些商店的贡献,不难发现一定是来自最后某一段后缀,那自然就有这个后缀不能有重复元素并且越长越好,那我们就需要找到一个方案,使得最后一次清空操作尽可能的早
考虑动态规划,求取每一个点 “能否在此处被清空
- 如果
,也就是最后一次清空商店后出现了该商店内有的元素,那么显然有 - 如果
,那么就没有重复元素,只能找 范围内有没有 号商店,如果有则 ,否则
如果
- 如果
,那么直接令 再进行接下来的调整,因为 在 之前必然会导致此次清空,是最坏结果 - 如果
,我们就令 为此后第一个 的位置,因为到这里就能处理掉所有前面带来的 “可能重复” 的元素
最后我们贪心的取一个最长的合法后缀统计答案即可
收获 & 反思
依旧考虑动态规划的本质:
- 你维护的东西能解决问题
- 你维护的东西没有后效性(是一个独立的子问题,且可以高效支撑后续求解)
并且考虑动态规划设计过程中的常见思想:
- 利用贪心来构建单调性,或是确认 / 简化维护对象,譬如这题里的 “最近一次清空商店的最早时间”
- 尝试挖掘一些性质来作为跳板
- 如果单一的变量不好维护或性质不良,那么就考虑拆分答案或者组合贡献来维护
- 必要时,使用数据结构来优化动态规划过程
12.17 - 1 - Problem - 79D - Codeforces
思路
非常厉害题,还有 plus 版本
我们考虑把这个问题一步一步转化,首先是常见的区间操作换为两点处的差分操作,还有就是操作显然可逆,所以考虑如何把目标序列转化为全零串
这样一来,原本的若干区间取反就变为了若干点对的取反,而我们不难意识到,如果要消去一个点对
接下来的问题就是如何把所有点都消去,显然,如果我们建立一个新图,图上只有需要被消去的位数,两点之间的边权就是这两点消去的代价,不难意识到答案就是这张图的最小权完美匹配,对于
收获 & 反思
常用的转化思路:
- 区间操作换为两点处的差分操作
- 若操作可逆,则考虑如何目标态转化为初始状态
- 消去点对,等价于一系列头尾相连的操作
[双倍经验(未完成)](P3943 星空 - 洛谷)
12.18 - 1 - Problem - F - Codeforces
神秘计数题
思路
首先需要借用一下之前某道题的小结论(历历在目)
就是我们考虑在
但是最终分出的组的总数并没有固定,所以我们不妨把所有的分法都用空集补齐到
乍一看转移需要的开销是
收获 & 反思
结论运用的很巧妙,好在之前接触过,倒是在复杂度分析那里卡了比较久,相关注意力有待提升
12.19 - 1 - Problem - G - Codeforces
并查集维护边双连通分量
思路
这题的关键是注意到固定三元组中的