CQOI 2022 游记
Day 0
下午旷课去试机,一切都还好,除了Dev貌似没法调试。虽然我也不用调试,但机房里有人要,所以不得不想办法。最后搞了两个方案,一个是一波玄学的删了再装(我也不懂怎么搞的),一个就是改用VScode(还有一个就是不调试)。不会VScode,所以就顺便找green orange学了,配置这东西确实麻烦(,但用起来也挺方便。
这两天意识到一堆东西没有复习,所以便不抱有太大的期望,反正NOIP已经考萎了。
晚上没有失眠,挺好。
Day 1
到yc后看了一下座位,坐在边角的愿望实现了(1号)。然后就是拍照啥的,没啥好说的。
进考场比较早,大概还有半个小时才开考,就打了点头(还是用的Dev)。
解码后看题,T1题目极长,大概浏览一下发现是之前说着玩的手写编译器,没想到省选考了。于是就估摸着模拟吧,如果复杂度不对再去想吧。于是就开始码。一开始没看到字符的区分,导致调了一会儿样例后才发现。大概45分钟后调完过三个样例。
然后直接看T2。感觉挺可做的,就去搞。但怎么搞都逃不掉复杂度里有个
T3其实是和T2交错看的,T3题目也比较长,粗看题面以为也是个大模拟。但看到后面意识到只是套了层处理字符串的皮。自认为是一个有向图的路径覆盖问题,好像有环,但没管那么多,就直接敲了个Dinic上去。第二个样例的最后一个测试点过不了,发现有的匹配的权值是
最后的时候发现T1有个奇怪的define替换成空字符,但没时间测试程序会不会萎了。undef的部分也没时间构造一点强数据测试。
中途我的键盘突然按不了了。重启后才好,大概耽搁了5分钟吧,还好影响不太大。鼠标有点硬,或许有一点点影响。
出考场后发现大家好像都只做了T1。pg说T2是拉插,突然感觉确实很像。然后他T2的暴力是
下午看群的时候突然感觉自己T1的细节挂了。感觉又T又WA,于是睡个午觉,做的梦全是T1炸了。
醒了后决定复习一下dp,顺便把VScode下在自己电脑上,磕磕碰碰勉强装好了。于是就一直找dp题做。半个下午加半个晚上写了6道dp,然而感觉还是比省选的难度水一些。
晚上吃的串串,没啥好讲的。依旧是没有失眠。
Day 2
起得略晚,但还好。要出发的时候找不到身份证了,结果又被夹在了一个小本子里。
到考场比较晚,但也不算太晚,至少大家都还在排队。座位号变了,坐在16号。
解码看题。感觉T1应该可以做出来,T2是WC T1的变形?T3是某年NOIP T4的变形?
之后就比较痛苦,简单来说就是罚坐(。T1想了很久,各种方向想,各种性质找,都没有找到很好的方法。以为要反演,但不知道该怎么搞。最后还是先打了个暴力拿点分(中途还是发现了一点性质,比如每个数最多贡献4个质数,但并没有实际用处)。
T2想往着WC的方向去想,先换成二叉树,但当时听的比较匆忙,没去细想那个构造。考场上临时推,发现怎么推第二种情况都没法转化。最后想到的也只能转化成多叉树,但一直怀疑,所以时间基本就浪费了。
T3先打了暴力。想了个按权值从小到大考虑的贪心,但感觉很不对劲,一堆细节没解决,以及正确性无法证明。
之后T1又打了点其他部分分的暴力,调了一段时间才过样例。
那段时间大概又想了个要用FMT的东西,但不熟,忘了怎么实现,复杂度也有点问题。
整场考试,我一点半方向有一个人一直在哼歌,有很大影响!
出考场,green orange爆切两道。其他人好像和我一样。lzy的T1用了状压,然而我的那部分是暴力枚举子集,现在想来可能会T。
总之是萎了。自己太菜。一堆知识点不熟,一堆题没见过。还需拼命整。
对了,周末作业还没做(。
Day 6
成绩大概前两天就出来了,我差不多也早就知道考砸了。至于多砸,Day1没上100,Day2爆零(虽然本地和虚拟机上都有分)。这些已经过去,最重要的是接下来这一年。大概现在最需要的是一个正确的方向。
附录
题解
Day 1
T1
思路很简单,模拟就好了。
主要是细节问题:
- 注意读题:有标识符这个东西,所以不要理解成了把
替换成一堆单词,中间用空格隔开(或许只有我这个**才会这么读题)。 有可能是空串,所以有的写法需要注意一下(比如我的写法就需要在define字符串连出去的字符串后面再连一个空串防止不展开)
其他也没啥好说的了,按照题目模拟就好了。
看似复杂度有点问题(我甚至用了 vector<string>
以及每次展开的时候都访问 map<string,int>
) ,但还是能过,复杂度卡不满,数据也比较水。
T2
其实并不太难,考场上就观察到答案大概是个
从暴力开始思考(下面记
最暴力的暴力:
题目给了极差,那么如果枚举最小值的话,最大值的范围就固定了,那么每个数的值域选择范围就确定了,那么对于一条路径,直接把所有点的值域大小乘起来就好了?显然不对,因为随便选的话选出来的最小值可能会大于枚举的。但容易发现,其实稍微容斥一下就好了。形式化地:计算一条路径
所以容易想到一个稍微优化了一点的暴力(也就是我考场上的写法):枚举一个端点,然后一边DFS一边算答案,每到一个点就枚举一下最小值。这样就可以
有一档部分分显然给的是
其中,
对于第二问,发现不太好直接做,但从贡献的角度出发,记
其实比较好好理解,就是都是算上
所以就可以
对于正解,肯定是考虑把复杂度里的
先考虑
这是一个不超过
对于第二问,就类似了,只不过每个点的贡献随最小值变化的函数要么是常函数,要么是二次函数,所以最后要插值的函数是一个不超过
于是,对于每一个相邻拐点都跑一遍整个算法就可以遍历完整个值域,共有
不是很好写,细节需要好好斟酌。
T3
建议想得特别清楚再写。
考场上其实把大体思路都想得差不多了,但是几个细节不会导致慌了,就连部分分都没拿到。
感觉有向图路径覆盖还是很好想的吧?把按顺序安排在一起可以产生贡献的两条信息连一条边,然后问题就变成了用尽量少的路径覆盖完这张有向图,把点拆成入点和出点,跑Dinic即可。
暴力建边的话边数是
但是这样做有两个问题:
- 形如
A B loushang
和B A louxia
的 两条信息安排在一起的话贡献是 ,于是就要跑费用流?跑费用流复杂度不太行。但可以发现,这样的两条信息强制安排在一起一定是不劣的,有多个角度的证明都说的通,这里就不赘述。强制安排后这两条信息是否就可以完全剔除网络流的建模中呢?显然不,因为它们可能还可以分别帮某些信息产生贡献,具体建模后面一起说。 - 这张有向图是有环的!所以算法假了?幸好没有完全假,如果不输出方案的话,答案也是对的,因为题目中那个反复强调的性质:每个人都至少会发一条学术信息 。利用这个性质,可以破环,并保证答案不劣。
接下来叙述具体的建模及破环的方法:
建模:
需要
- 对于所有信息
- 对于没有强制匹配的信息
- 若是楼下型信息:
- 若是楼上型信息:
- 若是学术信息,忽略
- 若是楼下型信息:
- 对于没有强制匹配或强制匹配了,自身是楼下型信息:
- 对于没有强制匹配或强制匹配了,自身是楼上型信息:
可以画个图手推一下搞懂原理,自然就会怎么找方案了。
破环:
把原始方案从跑完Dinic的图(这张图由链和环组成)中拎出来后,用学术型信息破环。首先发现环上的信息一定要么都是楼上型信息,要么都是楼下型信息。于是强制匹配的信息也肯定不在环里。假设一条环长这样
若该环全是楼上型信息,那么把连接方式换成:
若该环全是楼下型信息,那么把连接方式换成:
这样做之后所有学术型信息的
细节:
太多了,难以列举。
卡常:
建议按上面的顺序建边,会快不少。空间尽量比好开。尽量减少递归和STL(比如手写Dinic里的队列)的使用。反正因人而异,我常数大,所以不得不想尽办法卡常(。
时间复杂度:
花絮:建议别写 。反正我搞了很久,大概适合有较长时间闲着没事干的时候写。题目里面的楼上型信息和楼下型信息的定义很绕(比如A B loushang
到底该读成
反正我认为是一道整体思路不难,细节恶心的网络瘤。
Day 2
T1
首先需要注意到几件事:
的范围没有什么用,因为值域很小,开个桶来存数。- 每个数的贡献只和它的质因子集合有关。
- 一个数
的最大质数是 的,并且超过 的质数最多只有一个。 - 由上一条性质,除去
个数中每个数的最大质数,其他质数只有 个。 - 一个数
最多有 个互不相同的质因子(对此题没用)。
然后就可以做了。
先考虑暴力吧: 单次查询
然后容易想到容斥:先粗糙地用
其实上面一大段话都是在模拟容斥的过程,比较熟悉容斥的话其实就直接一个式子解决:
设
但这算法目前不能解决其他sub,这时候就可以利用性质了:第三条性质告诉我们,每个数最多有一个大于
但现在还有一个问题是如何快速的在每个集合
所以总的复杂度就分成预处理和查询两部分,长这样
注意点细节上的问题:
的幂次预处理,省掉一个- 不包含大质数的数不能简单的单独开一个集合表示,大概要用
来减包含的(或许只有我这个**才会这么干)。
T2
先把括号序列转化成树(或许只有我这个**在考场上忘了怎么转化,手推一堆构造),把一对括号当成点,类似这种(())
的关系就是父子关系,类似()()
的关系就是兄弟关系。
操作1是对于同一个点 (((((...)))))
,也就是一条链。
注意到应该从浅到深把点往下放最优,因为这样做的操作空间更大;然后对于每一层,其实都是想找一个点留在当前层,把其他点放到下一层,所以对于每个点,我们只关心它的深度,不关心它的具体位置。
输出 ,可惜没分。 ,只有裂解的点有代价,那显然每一层留下当前层权值最大的点不裂解,留在当前层即可。 ,依靠和裂解都有代价,那也很好想,对于每一层,前几次都依靠当前层权值最小的点(不要裂解权值最大的点),但是最后一次操作依靠权值最大的点,也就是把权值最大的点留在当前层。正确性的话比较显然吧。 ,只有依靠的点有代价。考虑把答案拆成两个部分:一个是每一层的最后一次操作的总代价,也就是把点固定在某一层的代价;一个是把点往下放的代价。然后要观察到一件事情,就是每层操作的点数随深度的增加,大概长这样 (类似凸的)。证明比较简单,在达到原树的最大深度之前,每层增加的点数至少为 ,而每层都会留下一个点,也就是减少一个点,那么总的点数就不减。达到最大深度后深度就显然单减了。然后对于前面的一段 ,显然是不用被操作的,就不管(之后的讨论都没管);对于 后面的 ,这是问题所在,先留个悬念;对于 之后的部分,每一层(最后两层无所谓,不用考虑)都可以把当前的最大值和最小值放下去。那么,如果没有 后面的那段 ,那么所有点中的最大值一定可以放到最下面的那一层去,而不产生第一类代价。这样显然最优,因为只有一个点可以不产生第一类代价(别忘了已经忽略了前面的一段 )。而对于第二类代价,都是由当前层的最小值贡献的,所以也更优。所以总结一下,如果没有 后面的 ,那么对于每一层,除了最后一次操作依靠最大权值,裂解最小权值以外,其他都依靠最小权值,裂解非最大权值。现在考虑如果有怎么办。首先要观察到从那一段 出来的点只有一个,然后结论是这个点要么是这些点里面权值最大的,要么是权值最小的。如何证明?很简单:首先,观察到总代价其实只跟当前层的最大值和最小值有关;然后现在想从那一段 里选出一个点来,那么它要么只能对最大值的那一部分产生贡献,要么只能对最小值的那一部分产生贡献,要么没贡献;于是,肯定最大值或最小值最优。于是就做完了(。
关于复杂度,四种情况分别为
T3
很神仙的树形dp了吧,状态设计很妙,转移也很妙,以下大部分内容基本来源于 这篇博客 (写得很好orz),对于一些内容加以完善,建议看式子的时候边画图便理解。
记
设
按
按
-
:叶子节点,根据定义,把该节点的出边砍掉,代价只算该节点,即 。 -
:记
为 的孩子。- 若
: ,意思是先砍掉 的出边,代价为 ,然后把 移到 ,再砍掉 (至于 ,想想就知道为啥了,下面的形如这样的条件的解释类似)。枚举 ,类似树上背包的分析,每对点只会在其 处转移一次,时间复杂度 。 - 若
: ,意思是先把 移出 ,增加 的代价,此时 在 处,再把 移到 处,之前没有算 的代价,所以要加上 乘其操作次数 。枚举 ,时间复杂度 。
- 若
-
:-
若
:记 为 在 一侧的孩子, 为另一侧的孩子。则删的顺序为 的入边、 的入边、 的入边。枚举 使得 从 移到 ,枚举 使得 从 移到 ,枚举 移动到的点 。则有:第三层
只和 有关,枚举 预处理。第二层 只和 有关,枚举 预处理。最外层 只和 有关,枚举 计算。时间复杂度 。 -
若
:记 为 在 一侧的孩子, 为另一侧的孩子。则删的顺序为 的入边、 的入边 、 的入边。枚举 使得 从 移到 ,枚举 移到的点 ,枚举 移到的点 。则有:计算方法类似前一种情况,两遍预处理之后再计算。时间复杂度
。 -
若
:记 为 在 一侧的孩子, 为 在 一侧的孩子。则删的顺序为 的入边、 的入边、 的入边。枚举 移到的点 ,枚举 使得 从 移到 。则有:两个
里的东西类似之前的情况分别预处理,然后再计算。时间复杂度 。
-
答案为
总复杂度为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】