APIO2020简要题解

去年考得稀烂.今年重新看了一下,发现还是不会做

\(A.paint\)

  • 题目链接:[APIO2020]粉刷墙壁
  • 概要:弱智\(dp\) + 语文阅读理解
  • 首先显然地有一个\(dp\),\(dp_r\)表示当前粉刷到\(r\),至少几次要求
  • 转移方程是显然的
  • \(dp_r = min_{k = r - M}^{r - 1}dp_k + 1\)
  • 这个显然可以通过单调队列做到线性,问题在于如何快速判断转移是否合法,即从\(r\)开始是否可以粉刷墙壁
  • 另外设一个\(dp\)数组\(F[i][j]\),表示粉刷到第\(i\)段墙,第\(j\)个人(可以先复制一倍解决环的问题),往前最多可以连续粉刷的段数.转移是显然的,然后前面的\(dp\)判断\(r\)是否合法,就等价于\(max_{j = 1}^{2*n}F[i][j] == M\)
  • 注意到题目保证\(\sum_{k}f(k)^2 \leq 4e5\)
  • 所以有效状态很少,\(hash\)即可

\(B.swap\)

  • 题目链接:[APIO2020]交换城市
  • 题意简述:简述不来,看题目吧.
  • 首先这种最大值最小的问题我们可以通过二分来解决,我们设当前二分的值为\(w\),并且设\(\mathcal{G_w}\)为只保留\(\leq w\)的边后重构的图
  • 我们考虑如何判断\(a\)如何在满足题目要求下到达\(b\),除了\(a,b\)\(\mathcal{G_w}\)中联通外,还需满足以下两个条件之一
  • 图中有环
  • 存在某个点度数\(\ge 3\)
  • 考虑用\(kruskal\)重构树来解决瓶颈路的问题,建出\(kruskal\)重构树,我们可以从\(a\)通过倍增找到相应节点,可以算出\(\mathcal{G_w}\)中,\(a,b\)所在连通块的点数和边数从而判断出是否有环,并且我们可以对每个点预处理出若要使其度数\(\ge 3\)最小的\(w\),设为\(g_u\),那么我们只需要维护\(f_u\),表示\(u\)的子树中最小的\(g_u\)即可.
  • 时间复杂度\(O((n + m)\log n + q \log^2 n)\)
  • \(upd:\)优化二分套倍增的部分改成直接倍增似乎可以做到\(O((n + m + q)\log n)\)

\(C.fun\)

  • 题目链接:「APIO2020」有趣的旅途
  • 首先我们考虑是否可以在\(4n\)次操作内将树还原出来,然后接下来就可以通过维护直径等方法通过这道题
  • 但是很遗憾,我不会\(O(n^2)\)以下的做法,也许有\(O(n\log n)\),但也无法满足\(4e5\)次的询问要求
  • 我们真的需要知道这棵树长什么样吗\(?\)
  • 考虑假设我们知道这棵树长什么样,我们可以怎么做
  • 找重心,注意此时不同的子树\(siz \leq floor(\frac{n}{2})\)
  • 假设度数只有\(2\),那么显然轮流选不同的子树中深度最大的点就可以了
  • 若度数等于\(3\),(题目保证任意点度数\(\leq 3\)),那么我们可以每次选和上一个子树不同的子树中最深的点,并将其删去那么一定存在一个时刻使得最大的子树等于其余子树的和,那么按上面的方法轮流选即可
  • 为什么这样是对的呢\(?\)考虑我们这个构造方法如果不满足题目要求会是什么情况,即只剩一个子树不为空,那么我们只需证明在这种情况来临前,一定会存在某一时刻使得最大的子树等于其余子树大小的和即可

\(proof\)

考虑设函数\(V:Tree->N,V(T) = 2 * maxsize - sum\\\)
注意到初始时由重心定义,\(V(T) \leq 0\\\)
若只剩一个子树不为空那么显然\(V(T) > 0\\\)
如果初始时\(V(T) = 0\),那么由于情况比较特殊,我们可以直接从取最大子树中最深的点转化为\(V(T) = -1\\\)
否则,注意到若存在一个时刻最大的子树等于其余子树的和,那么\(V(T) = -1\\\)
注意到按我们上述的方法,每进行一次操作,\(|\Delta V(T)| \leq 1\),即这个函数在整值上是连续的,所以若从\(<0\)变成\(>0\)一定会经过\(V(T) = -1\)这一个点,证明完毕

  • 那么我们发现,根据这种方法,我们真的需要知道整棵树长什么样吗\(?\)
  • 我们只需知道以下几点
  • 树的重心,并以它为根----\((1)\)
  • 每个点的深度----\((2)\)
  • 每个点在根的哪个子树内----\((3)\)
  • 考虑如何构造,注意到题目中提供第二种交互操作attractionsBehind(int X, int Y)可以知道以\(x\)为根,\(y\)的子树的大小,于是我们可以以\(0\)为根,得到\(\forall i,siz(i)\),那么满足\(n - siz_i \leq floor(\frac{n}{2})\)\(siz_i\)最小的\(i\)即为重心,这步需要消耗的操作是\(n\)
  • \((2)\)操作是显然的,也要花费\(n\)
  • 然后接下来是\((3)\)操作,这个比较复杂,我们考虑最差情况有\(3\)个儿子,\(p_1,p_2,p_3\)可以通过询问\(dis(p_j,i)\)\(dep_j\)作比较来判断\(i\)是否在\(p_j\)的子树内,但很遗憾如果直接这样做是\(3 * n\)的,和前面加起来就等于\(5 * n \ge 4e5\)
  • 但是假设我们设\(siz_{p_1} = a,siz_{p_2} = b,siz_{p_3} = c\),按顺序询问,然后如果确定了就\(break\),那么询问次数就是\(a * 1 + b * 2 + c * 3\),根据排序不等式,如果我们将\(p_j\)按照子树大小降序排序,那么是反序和以达到最小值,以最极端的情况来算,是\(a = b = c = \frac{n}{3}\),那么取到最差情况即\(\frac{n}{3} * (1 + 2 + 3) = 2 * n\),\(2 * n + n + n = 4 * n \leq 4e5\),可以卡着询问次数通过本题
  • 有一些细节详见代码:AC code
posted @ 2021-04-17 21:52  y_dove  阅读(237)  评论(0编辑  收藏  举报