zoukankan      html  css  js  c++  java
  • The rerooting technique

    rerooting 是求解一类与树相关的问题的技巧。这类问题的特征:

    1. 定义了一个以有根树为自变量的函数 $f$。
    2. 给你一个树 $T$。
    3. 需要求出函数 $f$ 在以 $T$ 的每个点为根所得到的有根树上的值。

    rerooting 在李煜东的《算法竞赛进阶指南》上名为“二次扫描与换根法”。

    记号

    $T_{r,u}$:树 $T$ 以点 $r$ 为根的有根树里以点 $u$ 为根的子树。

    $T_u := T_{u,u}$。

    设 $g$ 是一个关于有根树的函数。

    $g(r, u):= g(T_{r,u})$。

    $g(r) := g(r, r)$。

    Rerooting 的代码实现

    过程

    第一次扫描:通过典型的树上DP算出 $f(1)$。

    第二次扫描:深度优先遍历 $T_1$,算出余下 $n - 1$ 个点的 $f$ 值。

    代码框架

    #include <vector>
    const int max_n = 100000;              // number of vertices of the tree
    std::vector<std::vector<int>> g(max_n);// store the tree, vertices are numbered from 0
    
    void build_the_subtree_rooted_at(int u, int p) {
      for (int v : g[u]) {
        if (v != p) {
          build_the_subtree_rooted_at(v, u);
          // ...
        }
      }
    }
    
    // reroot the whole tree at u whose parent was p.
    void reroot(int u, int p) {
        // p lost a son u
        // u got a son p
        // modify u but DO NOT modify p
        // It suffices to calculate what p becomes if it lost the son u,
        // you don't have to actually modify p.
    }
    
    void dfs(int u, int p) {
        // compute f(u)
        // other things...
      for (int v : g[u]) {
        if (v != p) {
          reroot(v, u);
          dfs(v, u);
        }
      }
    }
    
    int main() {
      build_the_subtree_rooted_at(0, -1);
      dfs(0, -1);
    }
    

    解释

    计算 $f(u)$ 所需要的信息一般有两类,

    1. 依赖于 $T$ 和 $u$ 的全局信息,这类信息一般可以在第一次扫描的过程中计算出来。
    2. 依赖于 $T_{u}$ 的信息,把它记作 $g(u) := g(T_u)$,$g$ 是一个关于有根树的函数。

    reroot($u$, $p$) 的作用就是计算 $g(u)$。计算 $g(u)$ 所需要的信息可分成五类,

    1. $g(1, u)$,在 $g(u)$ 算出来之后即被 $g(u)$ 取代。
    2. 关于 $T_{1,u}$ 的除了 $g(1, u)$ 之外的其他信息。
    3. 关于 $T$ 的全局信息。
    4. $g(p)$。
    5. 关于 $T_{u,p}$ 的信息。

    第 1, 2, 3 类信息都是在第一次扫描过程中算出来的,在第二次扫描过程中保持不变。在调用 reroot($u$, $p$) 之前 $g(p)$ 已经算出来了,也保持不变。第 5 类信息是在函数 reroot 里由前 4 类信息计算出来的,只在计算 $g(u)$ 时用到,所以不必保存。总而言之,在 reroot($u$, $p$) 所作的唯一更改就是用 $g(u)$ 取代 $g(1, u)$。当然也可以不改变 $g(1, u)$ 而把算出的 $g(u)$ 另存他处。

    Examples

    CF1467E. Distinctive Roots in a Tree

    提交:116070391

    CF1092F. Tree with Maximum Cost

    提交:116069700

    POJ3585. Accumulation Degree

    提交:31002137

  • 相关阅读:
    数据库存储过程语法及实例
    springboot2配置JavaMelody与springMVC配置JavaMelody
    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userId' in 'class java.lang.Integer'
    ajax表单提交执行成功但是没有执行回调函数,并且post变get了
    SpringMVC——重定向跳转传值
    thymeleaf中跳转地址的使用
    solr安装与配置
    redis集群redis-cluster搭建
    nginx安装手册
    Linux忘记root用户的密码
  • 原文地址:https://www.cnblogs.com/Patt/p/rerooting.html
Copyright © 2011-2022 走看看