近年 APIO 选做

带有极强目的性的做题(

update on 2022.6.2:

  • APIO2022 之后来把 18, 16, 15 的补了,17 的咕咕咕了。
  • APIO2022 T1 降智 54 -> 21,T3 不会构造卡常痛失 8.64 分,输麻了。如果网上有题解了考虑补掉。

「APIO2015」

「APIO2015」巴厘岛的雕塑 Bali Sculptures

求把序列分为 \(x(x\in [A,B])\) 个区间,最小化权值,权值为所有区间和的或。

\(\texttt{Difficulty 2}\)

考验观察数据范围的仔细程度。按位贪心,发现只能设计个 \(O(n^3)\) 的 DP,又发现唯一 \(n\) 较大的满足 \(A=1\)

于是只需要最小化,又有一个 \(O(n^2)\) 的 DP,于是没了。

「APIO2015」雅加达的摩天楼 Jakarta Skyscrapers

序列上 \(n\) 个点,初始某些人在某个点上,他的弹跳能力为某个值。

对于在 \(x\) 上且弹跳能力为 \(y\) 的人,下一步只能跳到 \(x-y\)\(x+y\)(前提是位置合法)。

求传递信息从第 \(0\) 到第 \(1\) 个人所在位置的最小跳跃次数,中途如果两个人在同一个位置,则可以互相传递信息。

只有得知信息的人能动。

\(\texttt{Difficulty 3}\)

一开始想到了根号分治 + 无脑线段树优化建图,既要正反又要根据模数和弹跳能力讨论,大码量 + 大常数 + 巨大难调预定。

再一分析发现,用 \((p,q)\) 表示在位置 \(p\) 弹跳能力为 \(q\),那本质不同的二元组不就 \(O(N\sqrt N)\) 个吗。

那不是这题暴力老哥直接 win 了?写了写发现确实是这样。

「APIO2015」巴邻旁之桥 Palembang Bridges

题意复杂。

\(\texttt{Difficulty 2}\)

之前晚上回寝前看了个题面就跑路,第二天上课按照 \(k\leq n\) 想了一天,只会一个 wqs 二分 + \(O(n^2)\) DP 的鬼畜玩意。

感觉巨大没前途,然后越想越不可做,于是又回来看一眼题面,发现 \(k\leq 2\) 直接吐血。

那第一问显然因为对于选定的 \(p\)

\[\text{ans}=\sum |x-p|+|y-p| \]

所以就是中位数。

对于第二问,可以直接延续上述做法,发现对于 \(p\in[x,y]\) 的,没有多余贡献,否则:

\[\text{res} = \sum |2p-(x+y)| \]

按照 \(x+y\) 排序后枚举断点划分为左右两半,分别是两个 \(k=1\) 的情况。

预处理前后缀的权值,就是需要维护动态中位数,老经典的对顶堆了。

「APIO2016」

「APIO2016」划艇

给定序列上 \(n\) 个点的取值范围 \([a,b]\),每个点还可以选择不选值。

对于选值的点,要求构成严格递增序列,求方案数。

\(\texttt{Difficulty 3}\)

想了半天并不能脱离值域 DP,实际上大力离散化即可。

按照端点分左闭右开区间,那么离散化后,每种值要和每个点的取值范围,要么被包含要么不交。

所以能够简单的用组合数计算贡献,而这种区间又只有 \(O(N)\) 个。

「APIO2016」烟花表演

给定一棵树,可以花 \(\Delta v\) 的代价任意次修改任意边的边权,要求最终每个叶子到根的权值和相等。

求最小代价。

\(\texttt{Difficulty 5}\)

我的 slope trick 入门题,很早就听 IV 说过,之前尝试做过几次这道题,现在才真正补掉。

对于一个连续函数,满足线段的斜率为整数,且每次整体的斜率改变量为 \(O(1)\) 时,可以用断点来代表这个函数。

对于在 \(x\) 点的斜率改变量为 \(\delta\) 时,就在堆中加入 \(\delta\)\(x\)

一般(例如本题)\(f(0)\) 或者 \(f_{\min}\) 是可以简单维护的,也就顺势确定了整个函数,支持 \(O(n)\) 求出某个确定的 \(f(i)\)

函数的合并直接用可并堆,函数的改变需要简单讨论几种情况。

「APIO2016」最大差分

确定某个递增序列的最大邻项差值,只能查询 \([a,b]\),返回在区间 \([a,b]\) 里的 \(\min\)\(\max\)

限制次数为,设某次询问 \([a,b]\) 涵盖了序列中 \(x\) 个数,那么 \(\text{cnt}\gets \text{cnt}+x+1\),要求 \(\text{cnt}\leq 3n\)

\(\texttt{Difficulty 3}\)

另一种询问很平凡,而这种很巧妙。

直接花一次确定全局 \(\text{min}\)\(\max\),那么 \(\text{ans}\geq \lfloor\dfrac{\max-\min}{n}\rfloor\)

按照这个分块,只统计块间的答案即可。

「APIO2018」

「APIO2018」新家

给定商店的开业时间区间和地点,以及颜色。

定义某个时刻某个位置的代价为,“每种颜色到它的最小距离” 的最大值。

\(m\) 个不同二元组的代价。

\(\texttt{Difficulty 4}\)

有一定启发性。

做题的时候降智,先是看到时间区间无脑上了线段树分治,然后又分析错复杂度以为树套树硬上就行了。

然后发现实际上分治撤销的时候用到了删除,所以不如直接差分扫描线,而且实际上写了个 \(O(n\log ^3 n)\) 的区间数颜色 + 二分答案。

瓶颈在大常数的树套树,原因是需要数颜色,但是思考一下真的需要吗?

只需要确定每个区间里有没有所有颜色,既然已经记录了 pre,就应该想的等价于对于区间 \([l,r]\),没有 \((r,n]\) 中的 pre \(<l\)

此时就只需要单点修改和区间 min,再处理一下没有所有颜色的细节即可简单 \(O(n\log ^2n)\)

LOJ 讨论区里还被提出了一种单 log 的做法,大概是发现在线段树的结构上,强行套上二分答案是不明智的,应当直接线段树上二分。

「APIO2018」选圆圈

按半径降序 + 编号升序为一二关键字将圆排序,扫一遍序列。

若该圆还没被删除就删除它,以及和它有交(或相切)的圆。求每个圆在哪个圆被扫到时删除。

\(\texttt{Difficulty 3}\)

无脑上了 kd-tree,发现被卡了,然后看了看题解发现可以人类智慧旋转坐标系。

正确方法是一种有趣且据说很实用的 trick,就是将平面按照最大直径划分正方形网格,相交圆显然只出现在相邻格中。

对于每个点只暴力枚举相邻格中的圆,但是划分也要时间,大概每次最大直径减半时才重构即可。

复杂度 可以证明 是对的。

「APIO2018」铁人两项

求无向图三元组 \((a,b,c)\) 个数,满足存在 \(a\to c\) 且经过 \(b\)简单路径

\(\texttt{Difficulty 2}\)

圆方树入门题。

点双的优秀性质:对于一个点双中任意两点,它们所有简单路径的并集,恰好等于这个点双。

具体证明可以看 圆方树 - 粉兔

那么建出圆方树后,枚举 \(a,c\),那么 \(b\) 的方案数就是 \(a,c\) 之间路径上的点集大小。

给方点赋值点双大小,圆点赋值 \(-1\) 恰好能转化为路径点权和。直接拆贡献分开统计即可。

「APIO2019」

数据结构年,但阻碍不了我降智的脚步(

「APIO2019」奇怪装置

给定一些区间,对于数 \(t\)\(x=((t+\lfloor\dfrac{t}{B}\rfloor)\bmod A), y=(t\bmod B)\),求本质不同二元组 \((x,y)\) 个数。

\(\texttt{Difficulty 4}\)

这种近乎数学方面的小技巧积累的太少了,比如说联合省选2022 的 D1T2。

这种题目果断找循环节,首先 \(y\) 一样说明两个数一个是 \(p\),一个是 \(q=p+kB\)。然后对于 \(x\) 解方程得到:

\[k(B+1)\equiv 0\pmod A \]

最小的 \(k\) 显然为 \(\dfrac{A}{\gcd(A,B+1)}\),再乘上 \(kB\)\(B\),最小循环节就是 \(\dfrac{AB}{\gcd(A, B+1)}\)

就变成了环覆盖问题,直接差分排序一遍即可。注意 \(A\times B\) 有点恐怖,但如果 \(>10^{18}\) 就不用模数了,分开讨论即可。

「APIO2019」桥梁

维护带权无向图,支持:

  • 修改一条边的权值。
  • 求从一个点出发,经过 \(\leq v\) 的边能到达的点的个数。

\(\texttt{Difficulty 2}\)

当年之题,已成套路。

不带修直接离线并查集,有连边断边的可以套个线段树分治 + 可回退并查集。

带修就考虑操作分块,每次对于被修改的 \(\sqrt q\) 条边单独扫一遍处理。

其余只要保证每次的复杂度是 \(O(n)\) 的就行,还是挺暴力的。我的实现用到了排序还带一只 \(\log\)

对调块长的作用大感震撼,虽然理论很不对,但 \(O(\sqrt{n\log n})\) 的块长跑的飞快,\(O(\sqrt{q})\) 则慢的离谱。

「APIO2019」路灯

一排 \(n\) 条线段连接了 \(n+1\) 个点,两个点连通当且仅当之间的线段均被点亮。

支持两种操作:

  • 取反某条线段的状态。
  • 求从 \(0\) 时刻开始到该次询问时,\((a,b)\) 有多少个时刻是连通的。

\(\texttt{Difficulty 4}\)

感觉和 NOI Online 的 T3 是同道中人,就是偏序问题套了层大壳。

一只在想拆贡献,实际应该直接差分。

点亮和熄灭是类似的,对于线段 \(p\),都应当先找到 \(p\) 能到的最左点 \(l\)\(p+1\) 能到的最右点 \(r\)

然后如果是点亮,那么 \((l,p),(p+1,r)\) 这个矩形加 \(t-i\)\(i\) 是当前时刻,\(t\) 是每个询问的时刻,根据询问变化。

熄灭也同理,就是 \((l,p),(p+1,r)\) 矩形减 \(t-i\)

加上时间维是三维,每个矩形拆 \(4\) 个差分,就是经典三维偏序,直接 CDQ 即可。

极力推荐写 CDQ 题在拆完贡献后先写个暴力跑中型样例,保证拆贡献的准确性。

「APIO2020」

「APIO2020」粉刷墙壁

题意复杂。

\(\texttt{Difficulty 3}\)

考虑处理出哪些段能被表示,然后就是简单贪心覆盖。

然后比较降智的想了一堆假剪枝,实际上直接倒着 DP,滚动数组优化一下空间,有值的每次只有 \(O(\sqrt {40000})\) 个。

「APIO2020」交换城市

给定一张无向图,多次询问 \((s,t)\),有两个点分别走 \(s\to t\)\(t\to s\)

求两个点经过的路径的单个边权最大值的最小值,使得两点不会相遇。可以在节点处等待。

\(\texttt{Difficulty 4}\)

想到性质了,但是有点偏差。

实际上,就是从小到大加边,求两个点在同一连通块且连通块不是链的最早时刻。

然后还是可以魔改 Kruscal 重构树的过程,直接维护每个连通块变成非链的最早时刻,此时新建一个节点向连通块内的点连边。

因为需要维护集合中具体的点,所以要启发式合并并查集,同时维护链两端点的编号。

细节有点多。

「APIO2020」有趣的旅途

交互题。求 \(n\) 的排列,使得依次经过树上这些点,任意相邻两次移动距离,前一次 \(\geq\) 后一次。

可以询问:

  • \(\text{dis}(x,y)\):表示两点间距离。
  • \(\text{num}(x,y)\):表示有多少点 \(z\),满足 \(x\to z\) 的路径经过 \(y\)

两种询问加起来 \(\leq 4n\) 次。保证每个点度数 \(\leq 3\)

\(\texttt{Difficulty 4}\)

大概能想到固定一个点,然后在它的三个子树中横跳。

担心的是最终有一个子树剩了巨大多儿子,另外两个子树空了。

于是直接找重心,因为重心的最大儿子的 \(\text{siz}_0\) 满足 \(\text{siz}_0\leq \text{siz}_1+\text{siz}_2+1\)

其中取到等号的情况可以直接 \(\text{siz}_0\) 为一个子树,另外两个为一个,然后反复跳,最后跳到根,再跳到剩余的那个节点。

否则设 \(v=\text{siz}_0-(\text{siz}_1+\text{siz}_2)\)

如果 \(v=0\) 了,那么立刻可以采取上述策略。否则每次找最大深度的,不在上一次的子树里的节点作为下一个决策点。

这样不会有问题,因为如果出了问题 \(v>0\)(只剩一棵子树了),但是 \(\Delta v\in[-1,1]\),根据上述性质 \(v\leq 0\),故总有 \(v=0\) 的时刻。

确定重心可以 \(n\) 次得到:

  • 对所有节点询问 \((1,i)\) 得到 \(\text{siz}_i\),满足 \(n-\text{siz}_i\leq \lfloor\dfrac{n}{2}\rfloor\) 的最小的 \(\text{siz}_i\) 即为重心。

然后以重心为根,\(n\) 次问遍每个点到根的距离。

距离为 \(1\) 的为直接儿子,需要确定每个点在那个儿子里。

一次询问可以确定 \(x\) 是否在某个儿子中,问了两个都不在就一定在第三个里,所以是 \(2n\) 次。

需要注意一个细节,就是 \(v=0\) 时刻的突然转换可能导致 fun tour 的丢失。

原因解释起来比较复杂,但是画几种情况大概能想到。解决方法是在 \(v=-1\) 的时候就直接当两个子树做,最后剩一个和上面处理方法一样。

不难发现细节巨大多,如果没有 LOJ 的数据下载感觉这辈子调不出来(

「APIO 2021」

六边形那题不太会,也没见着网上有题解的,估计是不可做,所以暂时放弃。

「APIO 2021」雨林跳跃

给定一排 \(n\) 个点,每个点有高度 \(h_i\)

一个点可以跳到它右边最小的比它高的合法位置,或者它左边最大的比它高的合法位置。

多次询问 \([s_l,s_r],[t_l,t_r]\),求是否有 \(s\in[s_l,s_r],t\in[t_l,t_r]\)\(s\) 能够到达 \(t\)

如果有,还要输出最小步数。

\(\texttt{Difficulty 4}\)

各种贪心结论拼起来。

部分分有些启发性的就是 \(s_l=s_r\)\(t_l=t_r\) 或者只有后者的情况。

首先考虑起止点都是一个点,不难发现当且仅当 \(t\)\([s,t]\) 中的最高点时,有 \(s\to t\) 的路径。

但是显然不一定是从 \(s\) 开始无脑往右跳。

一个显然正确的贪心策略:

  • \(s\) 开始,在时刻保证 \(h_s<h_t\) 的情况下,往它能跳到的两个高度中的 \(\max\) 跳。
  • 再从当前的 \(s\) 出发,一直往右跳到 \(t\)

跳的过程均可用倍增加速。

当起点变为一段区间时,显然贪心的选择最大的 \(h_i\) 满足 \(i\in[s_l,s_r],(\max_{k=i}^{s_l} h_k)<h_t\)\(i\) 作为起点最优。

当终点变为一段区间时,对于 \(Q=5\) 的部分分可以直接枚举终点,这样就拿到了除正解外的所有部分分。

可以发现当前的策略已经很优了,所以大胆的进一步扩展结论。

对于终点,也贪心的钦定最小的 \(i\) 满足 \(i\in[t_l,t_r],h_i>(\max_{k=s_r}^{t_l-1} h_k)\) 为终点。

这样看似是正确的,所有从 \(<h_i\) 的点开始往右跳的点,第一个到终点区间的位置一定是 \(i\)

但是前提是从 \(<h_i\) 的点开始,显然还可能从 \(>h_i\) 的点开始,一步跳到 \(i\) 后面的情况。

于是再钦定一个终点,找到它的方法为:

  • 找到 \([1,s_r]\) 中最小的 \(>h_i\) 的后缀 \(\max\)\(j\)
  • 找到 \([t_l,t_r]\) 中最小的 \(>h_j\) 的前缀 \(\max\)\(k\)
  • 如果 \(k\) 合法,则钦定 \(k\) 为终点。

因为如果从某个起点开始,最早跳到的 \(>h_i\) 的点就是 \(j\)。而从 \(j\) 开始能够一步跳到 \(k\),一定是最优的。

「APIO 2021」封闭道路

给定一棵树,有边权。

对于 \(k\in[0,n-1]\) 的每个 \(k\),求出使得所有点度数 \(\leq k\) 的删边最小代价。

\(\texttt{Difficulty 5}\)

对于复杂度的严谨控制。

如果单次给定一个 \(k\),显然可以树形 DP。

\(f(u,o=0/1)\) 表示以 \(u\) 为根的子树都满足条件,\(u\) 已经删掉了 \(\text{deg}(u)-k-o\) 条边的最小边权和。

对于当前节点 \(u\),先将 \(\sum f(v,0)\) 作为决策,再将至少 \(\text{deg}(u)-k-o\) 个点变为 \(f(v,1)+w\)

显然可以将所有 \(f(v,1)+w-f(v,0)\) 排序,负数一定选,再从小到大选。

单次 \(O(n\log n)\)。但显然不能全局 \(O(n^2 \log n)\)

考虑对于 \(k\) 从小到大枚举求解。发现若\(\text{deg}(u)\leq k\),那么它的无需决策,可以全都不删。称这种点为无用点。

那么对于每条边 \((u,v)\),若 \(u,v\) 均无用则不用考虑,若只有 \(u\) 有用,那么只需要在 \(u\) 处考虑。

于是每次的决策形成了多个连通块,而每次遍历每个连通块的复杂度是可以接受的,因为 \(O(\sum \text{deg})=O(n)\)

考虑每个点维护一个大根堆,把无用点的边权直接扔到另一个对应点的堆里。

每次询问的时候照样树形 DP,一个关键点是若堆的大小 \(>\text{deg}(u)-k\) 就不断弹出,因为这些点以后都不会用上。

每次的 DP 的插入和撤销均可以暴力做,都是 \(O(\sum \text{deg})\) 的复杂度。总时间复杂度 \(O(n\log n)\)

具体实现上遇到了个大问题:

  • 我使用了 STL 的 multiset<long long> s 作为每个点的可删堆使用。

  • 我使用了类似如下语句 while(s[u].size() > lim) s[u].erase((-- s[u].end()))

    而注意到 \(\lim\) 可能为负数

  • 问题在于,size() 的默认返回值是 unsigned long 类型,所以必须强制转换 int,否则会出现任何正数都打不过 \(-1\) 的情况。

  • 一般 vector 循环枚举的时候不会有问题,因为都是在用非负数作比较。

  • 引以为戒!!!

posted @ 2022-05-20 13:52  LPF'sBlog  阅读(132)  评论(0编辑  收藏  举报