似乎大家反映觉得是 UOJ Extremely Hard Round? 其实出题的时候是觉得没有 UR 难所以叫 UER,不过得分分布好像打出了 UR 风范(
打雪仗
出题人 matthew99, whzzt, vfleaking
数据和题解 whzzt
咦为什么有三个出题人呢?其实是 myy 在群里扔了个 idea 我们觉得太简单了就稍微改了改,vfk为了方便选手通信又改了改,就变成现在这样了。看起来大家做这题还是很开心的。
算法零
提交样例程序,似乎通过了第一个测试点。
期望得分
算法一
咦我们有很多雪球!那我们直接让小
通信次数
算法二
小
小
这样做要通信多少次呢?
咦我们发现这个方案非常优!容易发现,如果最后不被选择的段中有
不幸的是,小
算法三
小
这时的期望通信次数是
算法四
from vfleaking
考虑将原本的01串等分成三份长度约为
显然这样做小
算法五
不妨考虑只进行一次通信,若小
即有
我们发现满足条件的最小
我们有一个概率算法可以达到这个界:考虑随机选择这样的
不过在本题中由于时间复杂度太高,无法实现。
雪灾与外卖
出题人 laofu
数据 whzzt
题解 laofu
算法一
爆搜,没啥好说的。
算法二
这是一个二分图匹配的模型。
可以发现一个显然的性质,餐馆和送餐员的匹配是不会相交的,即对于任意
这样就给我们提供了一个DP的顺序。
用
算法三
使用单调队列优化上述DP即可在
算法四
保证了餐馆的总容量,且
把餐馆和送餐员放在一起按照坐标排序,从前往后考虑,当考虑到一名送餐员
匹配完之后,我们可能进行调整,把这名送餐员改为匹配后面的餐馆。那么我们就可以撤销这次操作,然后把这名送餐员当做没有使用过,那么往堆中丢入
对所有餐馆和送餐员分别建一个堆来维护这个操作。复杂度
算法五
咋一看,是不是直接套之前的模型就好了呢?然而有一个问题:如果送餐员
那么,接下来
比如,如果送餐员在坐标
所以我们既要能够支持
复杂度仍然是
算法六
当餐馆的总容量无限制时,不能再把每个餐馆拆成一个一个单位容量的了。
考虑这样一个简化版的问题:
1.每名送餐员只能匹配在它左边的餐馆。我们求出最佳匹配的餐馆集合
2.每名送餐员只能匹配在它右边的餐馆。我们求出最佳匹配的餐馆集合
然后有一个结论:当
证明:我们对于数轴上任意一条边
而
算法七
我们试想,能不能继续沿用subtask6的那个贪心,从而把问题从subtask7转化为subtask5呢?
然而有这样一个反例:两名送餐员中间夹着两个餐馆,其中一个餐馆
为什么呢?因为我们在subtask6中证明的时候,在子问题
不过我们可以加一点小技巧:
1.在子任务六的子问题
2.在子任务六的子问题
猜想:原问题的最优解
关于该猜想,我们并没能给出证明或反例(基于对拍认为该猜想有较大概率正确)。如果能在WC之前证明做法的正确性或者举出反例的同学可以在WC中获得出题人提供的小礼品一份。
算法八
考虑算法五的过程。在过程中我们维护了两个堆,一个维护待匹配的送餐员,一个维护待匹配的餐馆。之前复杂度不对的原因是送餐员堆的复杂度没有保证,因为一个餐馆匹配了一名送餐员之后,这名送餐员可能反悔,所以这名送餐员又会重新丢入送餐员堆中。
但是注意到,对于送餐员
插播小广告:欢迎大家来听WC2019清新小课堂:模拟费用流问题。
许愿树和圣诞树
出题人、数据和题解 zx2003
算法一
直接模拟题意,每次有操作三的时候暴力dfs整棵树。
时间复杂度为
算法二
每次有操作三的重新dfs获得先序遍历太暴力了,考虑一些简单的优化。
我们在修改操作的时候顺便维护一下子树大小,然后每次操作三时就在树上二分。
这样单次复杂度就是
当数据随机时平均复杂度为
算法三
让我们思考一下,对于一棵二叉搜索树,在中序遍历已知的时候,我们还知道哪些信息,就可以唯一地确定它的形态呢?
根据操作三的提示,我们容易想到直接维护树的先序遍历。
通过简单的观察,我们可以发现,当一个点
并且对于任意两个小于
根据这个性质,我们可以定义“段”为权值为区间
对于子任务三,我们考虑对时间分块,每过一段时间就暴力重构整棵树的先序遍历。
然后我们暴力维护“段”构成的树结构即可。
复杂度
这个做法可拓展性有点差。
如果一定要拓展,那么就要用到后面的性质,然后效果相当于给后面的做法强行套上对时间分块。
算法四
让我们再思考一下,对于二叉搜索树,还有哪些信息可以用来辅助确定它的形态呢?
我们联想到了treap,或者说笛卡尔树。
我们可以给每一个节点赋一个键值,这样,上旋操作就等价于将键值调大至一个合适的值。
对于子任务三,由于每次都是上旋为根节点的儿子,所以每次可以轻易计算键值应该调为多少。
最后求遍序列的笛卡尔树即可。
时间复杂度为
算法五
让我们思考一下一个点在笛卡尔树里的父亲是什么。
或者更一般化,一个点在笛卡尔树里的祖先是什么。
冷静分析一下,可以证明,一个点在笛卡尔树里的祖先就是这个位置对应的前缀单调栈和后缀单调缀。
然后,对于子任务四,我们只要使用线段树简单维护每个点每个时刻的键值。
然后每次查询一个点的父亲时,就找出在序列中该点的前面第一个和后面第一个键值比它大的点,取键值较小者即可。
时间复杂度为
算法六
首先,由于每次不一定单旋到根,所以我们需要一个动态标号法,具体可以参考2013年陈立杰的集训队论文。
当然,由于本题不强在,所以我们可以离线后用个链表来维护。
然后对于前后缀单调栈的维护,这是个经典问题。
具体方法是对于线段树的某个节点,update的时候,用右儿子的最大值在左儿子的子树里二分,以计算左儿子的单调栈有多少被弹掉了。
这样就可以维护前后缀单调栈的长度。
至于询问第
直接写时
这样就可以通过子任务五了。
算法七
借助外界信息来维护树形态都不够优美。
实际上树的形态是可以直接维护的。
观察到如果是连续的三点共线的单旋,树的形态改变是很小的,直接把旋转的点提根即可。(HNOI2017单旋)
继续观察,发现一个点上旋的时候,树的形态变化大概是把这个点到根的左链和右链分别合并后作为点
并且,((这个点,该点的父亲,该点的祖先)三点不共线)的单旋次数是很少的。
可以证明是均摊
证明思路是,在splay的复杂度证明里,每次如果((这个点,该点的父亲,该点的祖先)三点共线),那么把单旋代价(1+势能变化)中的1给扔掉。
现在问题就变成了,如何
考虑直接用LCT维护整棵树,上旋x->y的时候,就提取出
然后通过在splay上二分来将x到y的路径分割成若干个权值单调的连续段。
将这些连续段分别拆开来后,按照一定顺序合并起来即可。
询问的时候在上LCT简单二分就行了。
复杂度直接算是