[BZOJ2599][IOI2011]Race
题目很简短:给定一棵树,边有权,求一条路径使得边权和等于K,且边数最少。第一次做树分治,Orz cxjyxx_me。
点分治大体思路
对于一个有根图的某个点,它的状态只有两种:
- 不在答案的路径上。这个时候就要递归处理它的所有子树,取一个最优答案。
- 在答案的路径上。
在答案的路径上的话,也有两种状态(不是每个题目都需要分开考虑):
- 这个点在答案的链的一端。可以DFS。
- 这个点在答案的链的中间。可以DFS每棵子树,然后合并两条链。
有意思的是,这个看似暴力的分治,在每次选取重心的时候,可以证明有O(nlogn)
的复杂度。所谓重心,就是以它为根的树中最大子树最小的那个点。实际做的时候并不需要每次都换根,只需要以任意点开始做,算出这样形态下每个节点的大小,这样,换到以i
为根的时候,只要多出一个n-son[i] (n为节点总数)
的子树即可。
这题的做法
因为K不是很大,所以可以开个桶记下来所有子树出现过的边权和,这样如果一个答案是T
,那么我们只要考虑K-T
即可。但是需要注意一个问题:我们并不希望出现两条链都在同一棵子树上的情况,所以我们在做DP的时候,要先更新答案,在更新用来统计边权的桶。
用dist[u].first
表示到达u点的路径长度,dist[u].second
表示到达u的边数。m[k].first
表示k这个边权最少需要几条边可以走到,m[k].second
作为时间戳,用来区别递归时的不同子树。