Gracekite's Homepage

Back

例题#

题目描述#

给定一棵有 个点的树,询问树上距离为 的点对是否存在。

输入格式#

第一行两个数 。

第 到第 行,每行三个整数 ,代表树上存在一条连接 和 边权为 的路径。

接下来 行,每行一个整数 ,代表一次询问。

输出格式#

对于每次询问输出一行一个字符串代表答案,存在输出 AYE,否则输出 NAY。

样例 #1#

样例输入 #1#

2 1
1 2 2
2
plaintext

样例输出 #1#

AYE
plaintext

数据规模与约定#

  • 对于 的数据,保证 。
  • 对于 的数据,保证 , 。
  • 对于 的数据,保证 ,,,,。

{% raw %}

{% endraw %}

基本思想#

点分治,顾名思义就是基于树上的节点进行分治。 如果我们在深入一点呢?对于点的拆开其实就是对于树的拆开。
所以我认为点分治的本质其实是将一棵树拆分成许多棵子树处理,并不断进行。
这应该也是点分治的精髓。
既然我们要将一个点进行分治,那么选点肯定最首要的。
思考下面一个问题:
如果树退化为一个链,
我们选取链首作为分治点,理论时间复杂度?
而如果选择链的中心,它的理论时间复杂度又是多少?
选择链首:
选择链心:
通过这个例子,我们不难发现:
如果选点后左右子树越大,递归层数越多,时间越慢,反之则越快。
所以我们的选点标准就出来了,而我们把有这个性质的点叫做:树的重心!

求解树的重心#

void getroot(int now,int fa){
    tree[now].size=1,tree[now].mx=0; //初始化当前树
    for(auto i : tree[now].to){  //遍历每棵子树
        if(i.to==fa || tree[i.to].usd) continue; //特判,防止死循环
        getroot(i.to,now); //递归查找根
        tree[now].size+=tree[i.to].size; //重新计算子树大小
        tree[now].mx=max(tree[now].mx,tree[i.to].size); //找出最大子树
    }
    tree[now].mx=max(tree[now].mx,tree[0].mx-tree[now].size); 
    /*此处 tree[0].mx-tree[now].size
    因为父节点也有大小;因此要将父节点算入
    */
    if(tree[now].mx<tree[root].mx) root=now; //优化选根
}
cpp

分治中心算法#

void divid(int now){
    calc(now);
    tree[now].usd=true;
    for(auto i : tree[now].to){
        if(!tree[i.to].usd){
            root=0;
            tree[0].mx=tree[i.to].size;
            getroot(i.to,0),getroot(root,0);
            divid(root);
        }
    }
}
cpp

{% raw %}

{% endraw %}

{% raw %}

{% endraw %}

{% raw %}

{% endraw %}

点分治
https://betteroier.github.io/blog/%E7%82%B9%E5%88%86%E6%B2%BB
Author Yuxuan Zheng
Published at August 22, 2022