codeforces 1452 G. Game On Tree

2020到2021的跨年之作,2021年的第一篇,也是我的第一篇 😄


题目大意:Alice和Bob在一棵结点数为N的树上玩游戏。Bob在K个固定的结点上摆放了棋子,Alice可以选择在某个结点上摆放一个棋子。他们轮流移动棋子,每轮的规则如下:

  1. Alice either moves her chip to an adjacent vertex or doesn't move it;
  2. for each Bob's chip, he either moves it to an adjacent vertex or doesn't move it. Note that this choice is done independently for each chip.

当Alice的棋子与Bob的一个或多个棋子处于同一位置,游戏结束。Alice希望游戏进行尽量多的轮数,Bob则相反。他们分别按最佳方案移动。输出N个整数,第n个数表示若Alice在游戏开始前将棋子放在结点n,游戏将进行多少轮。

官方题解


我们定义\(d(x,y)\)为x结点到y结点的距离。很显然,在Alice规划逃跑路径的过程中,知道Bob的追兵到达某个结点最少要花多久是很有帮助的。定义\(dis_x\)为x结点到距离最近的Bob的棋子的距离。一次多源最短路就可以处理出dis数组,由于所有边的长度相同,bfs就可以了。
考虑Alice的最佳策略是什么。设Alice的最初位置为a,若\(dis_a=\max dis\),待在a不动就是最好的选择了。然后我们通过大胆猜想不加证明把这种策略推广到一般情形:游戏一开始就赶往能够到达的dis值最大的地方,然后保持不动到游戏结束。
所以,为了得到答案,我们需要计算出从每个结点出发,能够到达的最大dis值。下面是一些有用的性质。


“从x出发可以到达y”有一个比较明显的充分必要条件:\(d(x,p)<dis_p\)对从x到y的路径上的每一个结点p都成立,也就是说,Alice不会在赶往y结点的路上被Bob拦住。进一步的观察可以发现,若\(d(x,y)<dis_y\),则\(d(x,p)<dis_p\)对于路径上的每个点p都必定成立。这样,要判断能否从x出发到达y,只要判断\(d(x,y)<dis_y\)是否成立就可以了。

另一个重要的观察是,\(dis\)在树上是连续变化的,i.e.,若\(p\),\(q\)之间有边相连,则\(|dis_p-dis_q|\leq 1\)
结合我们的上一个结论可知:若从x出发可以到达y,则对\(\forall t \in (0,dis_y)\),存在途中的某点p使得\(dis_p=t\)。这个性质允许我们用二分查找确定可以到达的最大dis值。


二分查找是个不错的想法,直觉告诉我们,这应当是正解了。但怎么快速实现呢?
官方题解中用点分治实现快速查询,根据实现思路,时间复杂度为\(O(nlog^2n)\)\(O(nlogn)\),欲知详情请自行查看。我有个另外的做法:整体二分+虚树DP
整体二分不难理解,关键是如何快速判断某个答案是否可行。假设当前需要判断一堆结点是否可以到达dis值为mid的结点。由于虚树保存了结点间的距离信息,我们可以把所有需要查询的结点和所有\(dis=mid\)的结点建成一棵虚树,然后再树上dp求得所需信息(最短路也可以代替dp,而且不知道为什么甚至比dp还快qaq)。
我的整体二分方法时间复杂度同样是\(O(nlog^2n)\)\(O(nlogn)\)。想要达到较优的复杂度,需要使用\(O(1)\)的LCA算法,并且注意预先按照dfs序排序,之后按类似归并的方法合并,以免排序拖累建树的时间复杂度。还是\(O(nlog^2n)\)的实现更方便一些呀。。。

这是我的提交记录:https://codeforces.com/contest/1452/submission/102705079
\(O(nlog^2n)\),用时有点悬 XD

posted @ 2020-12-31 23:26  dudw  阅读(167)  评论(0)    收藏  举报