【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\)。
然后用倍增(类似于倍增求\(LCA\))求\(dis_{x\to mid}\)。
找到每个点最近的标记点
这个要反过来考虑+换根\(dp\)才能\(O(n)\)。
换根\(dp\)模板题:洛谷P3478 [POI2008] STA-Station
\(Poi\sim\)
注意
- 求欧拉序一定要用\(dfs\),但是\(dfs\)会爆栈,所以要手动模拟栈……
- 倍增求\(LCA\)是卡不过去的。
\(\log\)不过10万,出题人没\(*\)。
Plan
以下列出我要写的步骤

浙公网安备 33010602011771号