点分治 模板题

本文介绍了点分治方法解决树上距离为k的点对存在性问题。首先解释了点分治的基本思想,通过寻找树的重心进行分治,然后详细阐述了解题过程,包括如何求树的重心,如何处理分治点以及如何通过容斥原理计算点对距离,最后给出了代码实现。
摘要由CSDN通过智能技术生成

题目描述
给定一棵有n个点的树
询问树上距离为k的点对是否存在。
输入输出格式
输入格式:
n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径
接下来m行每行询问一个K
输出格式:
对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)
输入输出样例
输入样例1:
2 1
1 2 2
2
输出样例1:
AYE
说明
对于30%的数据n≤100
对于60%的数据n≤1000,m≤50
对于100%的数据n≤10000,m≤100,c≤1000,K≤10000000

观察题目,很显然,这是一道点分治的模板题。
那么,我们就用点分治的方法解决它吧。
但是,点分治究竟是一种什么样的办法呢?
首先,就个人理解, 点分治是一种将树不断拆分处理的办法。而拆分树,我们则需要选择一个分治点,将树分成几部分分别处理。
我们该选择什么点进行拆分呢?
就像快排一样,如果选择的”中点”不优,快排的复杂度可以达到O(n^2),而点分治中,分治点如果选择的是端点,则复杂度会变成O(n)。
当然,快排的过程中,并不会一个一个数取检索来保证它选取的数一定是中间值,不过人们也可能会选择随机取数的方法来保证它的复杂度在一个较低的情况。

树的重心:
对于一颗n个结点的无根树,找到一个点,使得把树变成以该点为根得有根树时,最大子树的结点数最小。换句话说,删除这个点后最大联通块的结点数最小,那么这个点就是树的重心。
树的中心:
给出一棵边带权的树,求树中的点,使得此点到树中的其他结点的最远距离最近。

我们发现,如果我们能够得到树的重心,那么我们就能够以树的重心为分治点,让被切割后的所有子树尽可能平均,因为此点的最大子树的结点数最小。
那么,点分治的第一个重要操作,就是求取一个树(或者一颗被切割后的子树)的重心位置。

解法:任选一个结点为根,把无根树变成有根树,然后设f[i]表示以i为根的子树的结点个数。不难发现f[i]=Σf[j] (j∈s[i]) + 1 。程序实现很简单:只需要一次DFS,在无根树转有根树的同时同步计算即可。其实在删除结点i后,最大的联通块有多少个结点呢?结点i的子树中最大的有max{f[j]}个结点,i的”上方子树”中有n-f[i]个结点。

当然,我们也可以并不仅仅使用一个f[i]数组存储结点以i为根的子树的结点个数。因为这样的话效率相对可能会有所降低。另外增添一个数组记录g[i]表示以i为根的最大子树的结点个数,那么我们就可以得到g[i]=max{f[j]} ( j 是 i 的儿子) 然后再比较 g[i] 和 i 的祖先及其余部分的结点个数(也就是除了以i为根的子树以外的其他树的部分),就可以得到 g[i] 表示出以i为根的最大子树的结点个数。
通过比较所有的 g[i] ,也可以在 DFS(深度优先搜索)的过程中比较,来得到当前树的重心。


接着,我们就开始点分治的第二个重要环节。
点分治运用了容斥的思想。
我们使用点分治时,在寻找到重心后,将其作为分治点,先从分治点开始遍历一遍所有的点,并且计算出该树所有点距离分治点的大小,然后将它们两两组合记录下来,换句话说࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值