APIO2018~2022做题记录

1|0APIO2018~2022做题记录

1|11.[APIO2021] 封闭道路

题意:一棵大小为n的树,有边权,设f(x)表示要满足所有点的degx所要删掉的边的边权和的最小值,求出f(0)f(n)

思路:先考虑对于每个x计算答案。设dp[i][0/1]表示i向上连的边删或不删时的最小代价。转移时,对于i的每个儿子j,有两种贡献,a1=dp[j][0]+w表示删掉(i,j)的贡献,a2=dp[j][1]表示不删(i,j)的贡献。我们考虑先取所有的a2,用堆来维护所有的a1a2,然后选最小的一些a1a2a2替换掉,这一步可以用堆维护。单次复杂度O(nlogn)

然后考虑正解。如果从小到大考虑每个x,那么一个deg=x的点对[x,n1]是没有贡献的,对于每个点只需考虑x<deg的情况,这样的量级是deg=n的。我们将degx的点视为无用点,其他为有用点,然后从每个有用点开始dfs,把无用点视为叶子,这时每个无用点的a1=w,a2=0,它对它相邻的有用点的贡献即为a1a2=w,然后像暴力做法一样加入有用点的贡献,最后再加上一直弹堆顶到堆中元素为须删掉的边数时,堆中所有a1a2的和就是答案。注意更新答案后要撤销加入的有用点的贡献,并撤销不断弹出堆顶直到堆中元素符合要求时删除的无用点的贡献,需要用可删除堆维护。总(均摊)复杂度O(nlogn)

2.[APIO2018] 铁人两项

题意:在无向图上选3个不同的点a,b,c,要求从abc有简单路径,求方案数。

思路:考虑如果原图是一棵树,那可以简单地枚举b算答案,这启发我们建出原图的圆方树,再计算答案。对于一个点x,如果是圆点,只要选择的点不是在这个点为根的意义下的同一棵子树里即可,即sizy×(sizxsizy1);如果是方点,计算的是这个点双里的一个点为b,且a,c至少有一个在这个点双里的答案,即(degx2)×degx×(sizxsizy)。最终用换根dp实现就是O(n+m)的。

3.[APIO2019] 奇怪装置

题意:给定m[l,r],求不同的有序数对((i+iB)modA,(imodB))(i[l,r])的数量。

思路:设i对应的数对与j对应的数对相等,那么首先有imodB=jmodB,设i+iB=k1A+r,j+jB=k2A+r,那么有(ji)+jBiB=(k2k1)A,把j表示为i+kB,就有kB+k=(k2k1)A,自然,k最小可以取gcd(A,B+1),因此循环节就是A×Bgcd(A,B+1)

接着把每个区间对循环节长度取模,于是就变成了离线求区间交的问题,可以直接差分解决。

4.[APIO2019] 桥梁

题意:给一张无向图,维护改边权以及查询包含一个点且每条边的边权都不小于给定权值的连通块大小。

思路:考虑如果没有修改,可以直接把所有边和询问按权值排序,然后从大到小加边,用并查集维护连通块大小。但是有了改边权的操作,可以考虑将操作序列分块。对每一块的操作,先将所有边按边权排序,将询问按权值排序。对于每一个询问,先将没有修改且边权大于询问的权值的边加入,然后将在询问之前被修改的边加入,求出答案后删除,这一步可以用可撤销并查集维护。设以B为块长,那么复杂度为O(QBmlog2m+QBlog2n),块长取mlog2n可以做到O(QQlog2m)

5.[APIO2018] 新家

题意:数轴上有n个点,每个点有位置xi,颜色ti ,且在时间段[ai,bi]中出现。
Q个询问,每次询问在yi时刻所有颜色中距离位置li最远的颜色。某个时刻下位置li和颜色cj的距离定义为当时存在的所有颜色为cj的点与li 距离的最小值。

思路:首先考虑对时间轴离线,那么就变成了动态加删点和查询最短的包含所有颜色的区间的长度。对于区间长度可以用二分来求,那么问题变成了如何判定一个区间是否存在所有颜色。这里直接采用一个经典的区间数颜色套路,对每个点上的每种颜色,维护它的前驱,那么如果区间[l,r]包含了所有颜色,则区间[r+1,n]的所有前驱都在[l,n]中,即前驱的最小值不小于l,这样就可以简单判定了。为了方便判定,可以在n+1处加入每种颜色的点,而且由于每个点可以有多种颜色,需要用muiltset来维护前驱。时间复杂度O(Qlog2n)

6.[APIO2019] 路灯

题意:有长度为n+1的链,初始联通,接下来的每一时刻,会有一条边的状态改变,或者查询点a和点b在初始到现在有多少个时刻是联通的。

思路:发现每修改一条边,会有一个矩形的值在该时刻被改变,于是可以考虑把操作放到矩形上,设点i往左能到的最远的点为sti+1往右能到的最远点为en,那么ii+1联通时,把矩形[st,i],[i+1,en]加上Tt,断开时减去Tt,那么每一段联通的贡献刚好是Δt,查询也直接是单点查。对于连续段,可以用set维护,而矩阵加可以用树状数组套线段树维护,复杂度Qlog2n

7.[APIO2022] 排列

题意:构造一个排列,使得其上升子序列个数为kk1018,len90

思路:首先考虑连续上升的一段序列,可以产生2len个上升子序列,然后对k二进制拆分,每一位上的1对应一段,这样可以用log2k的长度解决。我们发现,这样做很浪费,明明可以不用每次都重新用一段,于是可以考虑在最长的一段后面接几个点,这些点可以和这一段的一个前缀共用子序列,就像这样img

可是这样长度是2logk的,还是超过了90,于是我们考虑怎么优化。我们发现,像这样,img图中的绿、蓝两点,对应的是k的二进制下相邻的两位1,但是我们可以用一个紫色的点来直接代替这两个点img按照这个思路,我们就可以做到1.5logk的长度来解决这个问题了。

8.[APIO2020] 交换城市

题意:有一张带权无向图,每次询问有两个人从x,y出发,要求这两个人不能同时在一个点上,也不能同时经过一条边,允许多次走一个点或者一条边,要求最小化路径上的最大边权。

思路:一看到最小化路径上的最大边权,自然想到了最小生成树,于是考虑在Kruskal重构树上下文章。首先,如果要满足题面的条件,充要条件是路径不是一条链,于是问题转化成了怎么判定。我们对于每个点,维护这个点所在所在的连通块是不是链,如果是链还要维护链的链头、链尾。现在,如果加入了一条边(x,y),根据x,y所在连通块是不是链分类讨论:只要有一个点所在的连通块不是链,那么这个新连通块就不是链;反之,如果两个点都是链的端点,那么这两条链会合并,否则就会形成一个新的不是链的连通块。对于询问,答案就是两点在Kruskal重构树上的lca的点权。复杂度O(mlogm+qlogn)

9.[APIO2022] 游戏

题意:一张n个点的有向图,初始对于0ik2,有边ii+1,且前k个点称为关键点。现在会加入一些边,每加入一条边查询是否存在一个包含关键点的环。

思路:假如存在环,那么环与主链01k1的交是一段子链LL+1R,且R能到L。考虑如果子链包含边(i,i+1),那么说明[i+1,k)能到[0,i],于是考虑维护两个点集。设[1,i]能到的点集S1[i+1,k)能到的点集S2,这样如果最终环不包含边(i,i+1),那么换上的所有点一定同时属于S1或同时属于S2。设一个点的状态为0/1/2表示不属于S1,S2和属于S1和属于S2,那么,只有一条边的端点都属于S1S2时,才需要进入子区间,于是考虑分治。

对于一个区间[l,r],设El,r表示对这个区间有影响的边集,那么对于一条边(x,y),有3种情况:

1.x的状态为2,y的状态为1,那么显然这时已经存在环了,可以直接退出。

2.x的状态为2,那么从y开始dfs,把经过的状态为0的点状态改成2,删除所有经过的边,将其和(u,v)下放到El,mid

3.y的状态为1,那么从x开始在反图上dfs,把经过的状态为0的点状态改成1,删除所有经过的边,将其和(u,v)下放到Emid+1,r

这样就只剩一个问题,环和主链可能没有交边,那么把u加1,把大于等于kv加1,并钦定主链为01k,这样所有以关键点开头,以关键点结尾的路径编号都加1,这样就和主链有交了。

10.[APIO2018] 选圆圈

题意:平面上有n个圆圈,每次会选择半径最大的圆并删掉它已经所有和它有交的圆,求出每个圆是被那个圆删掉的。

思路:我们发现,解决这个问题的关键肯定是选择了半径最大的圆后减少枚举其他圆的量。但是这是在二位平面上的,怎样可以减少枚举量呢?我们考虑按照最大圆直径把原图划分成一个个正方形的块,这样与最大圆有交的圆一定出现在这个方格以及相连的8个方格里,就可以大大减少枚举的量。但是问题又来了,如果最大圆半径过大,那么接下来的枚举量还是很大,于是考虑重构,如果当前圆的半径小于方格大小就重构,这样每一次重构边长至少会减半,只会进行logn次重构,而且可以证明每个圆被枚举的次数是常数,于是在O(nlognlogR)的时间内解决了这个问题。

11.[APIO2020] 粉刷墙壁

题意:题意太长了,不想写了。。。

思路:发现如果我们确定了哪些点可以作为开头,我们就可以直接贪心求出答案,现在问题就变成了如何判断一个点是否能作为开头。我们考虑一个朴素的dp,设fi,j表示现在用j号承包商涂第i号墙,往后最多能涂多少。这样如果一个点的dp值不小于m就说明可以作为开头。可是这样dp是O(nm)的,于是我们考虑优化。题目中有f(k)2400000,这就代表着每一段墙壁最多可以被n个承包商粉刷,于是可以考虑只记录可以粉刷这一段墙壁的承包商的dp值。在实现上,就是用滚动数组,且每次只更新可以粉刷这一段墙壁的承包商的dp值即可。

12.[APIO2020] 有趣的旅途

题意:有一棵树,每个点度数不大于3,你需要给出一个排列,使得dis(a0,a1)dis(a1,a2)dis(an2,an1),你每次可以询问两点距离和一个点为根时另一个点的子树大小,要求询问次数不大于4n

思路:容易发现,以重心为根,可以每次选择当前子树最深的点,跳到另一个子树最深的点,根据重心的性质一定有解。于是第一步是找重心。这一步可以先钦定0为根,求出每个点子树大小sizx,那sizx>n2sizx最小的点就是根,如何可以求出每个点深度以及在哪棵子树里,这些总共不到4n。然后根据有几棵子树分类讨论。两棵的情况可以直接处理,三棵的情况要根据最大的子树大小是否等于剩下两棵之和分类,如果相等,把剩下两棵合并后按两棵子树的方法处理即可,否则删掉深度最深的点。

13.[APIO2021] 雨林跳跃

题意:长为n的序列,每个点有高度且互不相同,每个点可以跳到左、右第一个比它高的点,求从[A,B]中任选一个起点,跳到[C,D]中任意一个点的最小步数。

思路:首先,每个点能一步跳到的点可以直接用线段树二分求出。我们先考虑A=B,C=D的情况,肯定是每次跳到比h[C]小且最大的点上,这样一定最优。再考虑更一般的情况,首先可以发现,如果[B,C1]的最大值大于[C,D]的最大值,一定跳不过去,因此这样无解。对于有解的情况,可以先假设从[A,B]中的最大值所在位置x开始跳,这样一定最优,不过如果[x,B]的最大值大于[C,D]的最大值就跳不过去,于是只能从满足[x,B]的最大值小于[C,D]最大值的最左的点开始跳,只要跳到的点不到[C,D]的最大值就可以跳,否则就只能一路向右跳,只要跳到[C,D]中即可。在实现上,求满足[x,B]的最大值小于[C,D]最大值的最左的点可以也用线段树二分,跳到过程需要两个倍增数组。复杂度O(nlogn+qlogn)


__EOF__

本文作者Xttttr
本文链接https://www.cnblogs.com/Xttttr/p/17399980.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Xttttr  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示