zoukankan      html  css  js  c++  java
  • 点分治笔记

    我们常用数据结构处理一些序列上的问题。这些问题的形式一般是给定序列的两个位置L和R,在区间[L,R]上执行查询和修改指令。如果给定的是一棵树,要求查询树上两个节点x和y路径的答案。点分治可以在一棵树上,对具有某些限定条件的路径静态地进行统计  

    【例题】 Tree (Poj 1741)

        给定一棵有N个点的树,每条边都有一个权值。树上两个节点x与y之间的路径长度就是路径上各条边的权值之和。求长度不超过K的路径由多少条。

      

        本题树中的边是无向边,我们称之为无根树。也就是说,我们可以任意指定一个节点为根节点,而不影响问题的答案。

       若我们指定节点p为根,则对p而言,树上的路径可以分为两类:

        1.  经过根节点p

        2. 包含于p的某一颗子树中。

        根据点分治的思想, 对于第2类路径,显然可以把p的每颗子树作为子问题,递归进行处理。

       而对于第一类路径,可以从根节点p分成“x~p”与“p~y”两段。

      我们可以从p出发,对整棵树进行DFS,求出数组d,其中d[x]表示点x 到根节点p 的距离。 同时还可以求出数组b, 其中b【x】表示点x属于根节点p的那一颗子树,特别地,令b[p] = p。

      那么此时满足题目要求的第一类路径就是满足一下两个条件的点对(x,y)的个数:

        1.  b[x] != b[y]

        2. d[x] + d[y] <= k

      我们定义Calc(p)表示在以p为根的树中统计上述点对的个数(第一类路径的条数)。Calc (p)有两种常见的实现方式。 针对不同题目,二者各有优劣。

      方法一: 树上直接统计

      设p的子树为S1, S2, ..., Sm

      对于Si中的每个节点x, 把在子树S1, S2, ... Si-1 中的满足d[x] + d[y] <= k 的节点 y 的个数 直接累加到答案中即可。

      具体来说,可以建立一个树状数组,依次处理每一个子树Si。

        1. 对于Si中的每个节点x, 查询前缀和 ask(k - d[x]) ,即为所求的y的个数。

        2. 对于Si中的每个节点x,执行add(d[x], 1),表示与p距离为d[x]的节点增加了一个。

      按子树一颗一颗进行处理保证了b[x] != b[y], 查询前缀和保证了d[x] + d[y] <= k.

      需要注意的是,树状数组的范围和路径长度有关,这个范围远比N要大。而本题中不易进行离散化。一种解决方案是用平衡树代替树状数组。以保证O(NlogN)的复杂度,但是代码的复杂程度显著增加。所以本题更适合用下一种方法。

      方法二: 指针扫描树状数组。

  • 相关阅读:
    LeetCode100-相同的树
    LeetCode66-加一
    LeetCode102-二叉树的层序遍历
    dubbo协议端口
    http错误-413 Request Entity Too Large
    【Jeecg Vue】通过getAction的finally来最大程度避免影响主数据呈现
    图片压缩,用这个就够了
    信息数据安全,日常办公你就要注意这些!
    java笔记:流式编程 数组与List集合互转
    改状态,你会改吗?你真的会改吗?
  • 原文地址:https://www.cnblogs.com/juruohx/p/15141614.html
Copyright © 2011-2022 走看看