点分治 理解及例题

点分治,基于点的分治;

其思路为:

子树结构:子树结构虽然的确是某点的一个子树,但我们讨论点分治时,相当于把这个子树摘下来,当做无根树处理;

对于一个子树结构:

  1. 处理子树结构的某个单点(未必是原根);
  2. 以这个被处理的点为新根,找出她的子树,变成递归下一层要处理的子树结构;
  3. 递归下一层;

在一个常见的,约定俗成的写法下(就是一个“说点分治就是指她”的写法)

其时间复杂度可以达到O(logn*f(num(1))

(num(i)为处理第i个单点时的数据范围);

(f(i)为处理数据范围为i的单点的时间复杂度);

(条件是f(i)是线性的);

这个复杂度是因为:

在递归分治的过程中,

对于每层,其num(i)之和与num(1)是同级的,

由于f(i)(往往)是线性的,故可以认为每层的各点的f(i)之和与f(num(1))同级,

在此基础上,我们使用里一种巧妙的办法,使得分治层数不超过logn层;

于是达到了需要的效率;

所以这个神奇的方法是什么呢?

考虑把每层的单点最大数量级,都变成上一层的一半;

这样就能使分治层数不超过logn时就不能再分了;

如何做到这点呢?

首先定义子树结构的重心:

在点分治中若在本层处理点i,可以使下一层处理的最大子树的节点最少,则点i为该子树结构的重心;

可以看出重心的节点最多的子树的节点数不会超过原子树结构的一半;

证明:

若重心的节点最多的子树的节点数超过了原子树结构的一半;

那么你看看以她的这个子树的根取代她作为重心的情况;

发现反而更有重心的样子;

于是这个重心是非法的;

所以重心的节点最多的子树的节点数不会超过原子树结构的一半;

于是我们用现在得到的方法重新修订、精细点分治的流程:

对于一个子树结构:

  1. 处理子树结构的重心;
  2. 以重心为新根,找出她的子树,变成递归下一层要处理的子树结构;
  3. 递归下一层;

这就是一个常规的约定俗成的却又效率优秀的点分治啦;

然后是一些实现:

分治的主体可以由dfs实现,

但跳转时,不会是直接跳到当前点的子节点,而是跳到当前点的子树重心上;

求重心:

两遍dfs实现:

inline void dfs_size(int now,int fa){
    size[now]=1;
    for(int i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].to!=fa){
            dfs_size(e[i].to,now);
            size[now]+=size[e[i].to];
        }
}
inline int dfs_root(int now,int fa,int r){
    int i,root=-1,wroot;
    max_size[now]=size[r]-size[now];
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]&&e[i].to!=fa){
            if(size[e[i].to]>max_size[now])
                max_size[now]=size[e[i].to];
            wroot=dfs_root(e[i].to,now,r);
            if(max_size[wroot]<max_size[root]||root==-1)
                root=wroot;
        }
    if(max_size[now]<max_size[root]||root==-1)
        root=now;
    return root;
}

例题:

POJ P1741 Tree

题解

时间: 2024-11-05 07:20:47

点分治 理解及例题的相关文章

关于点分治的理解

[引言] 由于树具有一般的图没有的特点,所以在竞赛中的应用更广. 在一些树上路径问题中,暴力求解时间复杂度过高,往往需要一些更为高效的算法,点分治就是其中之一. [流程] 1.首先选取一个点,把无根树变成有根树. 那么如何选点呢?    ——树型动规 因为树是递归定义的,所以我们当然希望递归的层数最小. 每次选取的点,要保证与此点相连的结点数最多的连通块的结点数最小,我们把这个点叫做“重心”. 那么找到一颗树的重心有以下算法: (1)dfs一次,算出以每个点为根的子树大小. (2)记录以每个结点

点分治——POJ 1741

写的第一道点分治的题目,权当认识点分治了. 点分治,就是对每条过某个点的路径进行考虑,若路径不经过此点,则可以对其子树进行考虑. 具体可以看menci的blog:点分治 来看一道例题:POJ 1741 Tree 题目大意:扔给你一颗有权无根树,求有多少条路径的长度小于k: 解题思路:先找出重心,用一次dfs处理出每个点到根的距离dis,然后将dis[]排序,用O(n)的复杂度处理出"过根且长度小于等于k的路径数目",删除根节点,对于每棵子树重复上述操作. 注意要去重: 像上面这样一个图

[bzoj1468][poj1741]Tree_点分治

Tree bzoj-1468 poj-1741 题目大意:给你一颗n个点的树,求树上所有路径边权和不大于m的路径条数. 注释:$1\le n\le 4\cdot 10^4$,$1\le m \le 10^9$. 想法:GXZlegend给高一将点分治,去听了之后的第一道模板题. 我们对于一类树上统计问题,除了强大的树形dp之外,我们还有分治.今天听的是点分治: 就是说,我们将所有的链关于一个点分划成两类:过这个点的链,和不过这个点的链.这个点就是根节点,我在任意两颗子树中拎出两个点,他们之间的链

点分治+动态点分治

最近好颓废,什么都学不进去... 感谢两篇:AKMer - 浅谈树分治  言简意赅 LadyLex - 点分治&动态点分治小结  讲解+例题,学到很多东西 点分治 动态点分治 ~ 点分治 ~ 经常遇见一类树上的计数题,问的是在某些条件下,选择一些点的方案数 若对于每个点的统计都需要遍历以其为根节点的子树,普通的做法就是$O(n^2)$的,在很多时候是不满足要求的 而这是点分治的专长 点分治是这样进行的: 1. 找到当前树的重心 2. 将重心及重心连出的边全部删去,那么就能将原来的树分割成森林 3

自适应学习

自适应学习是指根据学习内容和学习方式的不同,可以将人的学习分为三种不同的类型,它们是机械的学习.示教的学习以及自适应的学习.自适应学习通常是指给学习中提供相应的学习的环境.实例或场域,通过学习者自身在学习中发现总结,最终形成理论并能自主解决问题的学习方式. 自适应学习,又可以分为: (1)发现学习.提供的学习材料是一些未经分类的事例或未经整理的经验数据,学习者的任务是从这些事例或数据中发现概念或规律. (2)解释学习.提供的学习材料是一个概念.该概念的一个例子和有关规则,学习者的任务是首先构造一

屯题... 8.3

2015-08-03 00:12:31 ... SCOI 1293 - 1298 ... CDQ 分治论文.例题

夏令营讲课内容整理 Day 2.

本日主要内容是并查集和堆. 并查集 并查集是一种树型的数据结构,通常用来处理不同集合间的元素之间的合并与查找问题.一个并查集支持三个基本功能:合并.查找和判断.举一个通俗的例子,我和lhz认识,lhz和hzc认识,那么也就可以断定我和hzc认识. 依照并查集的思想,我们把所有要待处理的元素a1,a2,a3....an这n个元素都看作是一个单独的集合,初始状态每个集合都只有一个元素.我们就可以把并查集的合并操作理解为集合之间的取并集操作. 作为一个树形结构,在一个由许多这样的集合构成的森林中,每个

分层图最短路

分层图最短路,就是在分层图上解决最短路问题一般模型为:在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线常规思路:想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次......k次这样实际我们可以把这k个点想象成对应dp的不同的状态dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线我们用to表示要到达的点,x表示父亲节点,就有dis[to][j] = min(dis[x][j] + val(x, to), di

置换群和Burnside引理,Polya定理

定义简化版: 置换,就是一个1~n的排列,是一个1~n排列对1~n的映射 置换群,所有的置换的集合. 经常会遇到求本质不同的构造,如旋转不同构,翻转交换不同构等. 不动点:一个置换中,置换后和置换前没有区别的排列 Burnside引理:本质不同的方案数=每个置换下不动点的个数÷置换总数(一个平均值) Polya定理:一个置换下不动点的个数=颜色^环个数.(辅助Burnside引理,防止枚举不动点复杂度过高) 这篇文章写得很详细了(具体的在此不说了): Burnside引理与Polya定理 **特