【2020.11.25提高组模拟】小 T 与灵石(stone) 题解

【2020.11.25提高组模拟】小 T 与灵石(stone) 题解

题意简述

给一棵根为\(1\)的树。一共\(q\)次操作,每次选\(k_i\)个节点\(p_1,p_2,\dots,p_{k_i}\)。设\(f_{x,i}\)表示\(k_i\)个点中所有点\(p_j\)到点\(x\)的最远距离,即\(max_{j=1}^{k_i}{dis(x,p_i)}\),再设\(g_x\)表示\(q\)次询问中\(f_x\)的最小值,即\(min_{i=1}^{k_i}{f_{x,i}}\)。对于每个点\(x\)求出\(g_x\)

\(1\le n,q\le3\times 10^5,\sum_{i=1}^qk_i\le10^6\)

Solution

每次操作只有选的点中最远的一对点(可理解为标记点集合的直径)会对点x有影响。这个可以用直径的定义:图中最远点对 来证明。再把这个直径点对合并成一个点,即新建直径中点(若在边上就把边拆开),标记这个点。为了避免小数,只需把原本每条边的边权乘以2。

新建了树以后,原问题转化为了求每个点到标记点的最短距离,直接搜索即可。

那么,我们需要做的操作有:

找出给定点集的直径

直径两端点的其中一个端点一定是所以给定点中深度最大的点,另一个就用\(LCA\)求距离来找。

若用\(O(n)\)算法找直径的话,会超时。

倍增\(LCA\)\(O(\log)\)的,理论很不错但是就是过不去。

更好的方法:用欧拉序\(O(n\log n)\)预处理,\(O(n)\)查询求\(LCA\)接下来就写这个!,而且要手动数组模拟栈\(dfs\),递归\(dfs\)会爆栈。

将直径两端点缩成一个新点

设直径端点为\(a\),\(b\),直径中点为\(mid\)

\[max(dis_{x\to a},dis_{x\to b})=dis_{x\to mid}+dis_{a\to b} \]

然后用倍增(类似于倍增求\(LCA\))求\(dis_{x\to mid}\)

找到每个点最近的标记点

这个要反过来考虑+换根\(dp\)才能\(O(n)\)

换根\(dp\)模板题:洛谷P3478 [POI2008] STA-Station

\(Poi\sim\)

注意

  • 求欧拉序一定要用\(dfs\),但是\(dfs\)会爆栈,所以要手动模拟栈……
  • 倍增求\(LCA\)是卡不过去的。
  • \(\log\)不过10万,出题人没\(*\)

Plan

以下列出我要写的步骤

  • 模拟栈$dfs$求深度和欧拉序
  • $ST$表预处理$LCA$
  • 倍增$LCA$求直径中点
  • 新建直径中点
  • 换根$dp$
  • Code

    posted @ 2020-11-27 15:34  Vanilla_chan  阅读(150)  评论(0)    收藏  举报