• Codeforces 1486F Pairs of Paths


    Description

     给定一棵大小为 $n$ 的树和 $m$ 条链 $(u, v)$。统计有多少对链,满足这两条链恰好有一个交点。

    $n, m le 3 imes 10^5$

    Solution

    不妨钦定 $1$ 为根分析。

    考虑如下问题:恰有一个交点的两条链会长成什么样子。

    其实分为两种情况:一是它们的 $ ext{LCA}$ 相同,而是它们的 $ ext{LCA}$ 不同。如下图所示。

    左图中 LCA 表示两条链 $A, B$ 公共的 $ ext{LCA}$,而右图中 LCA 则是 $A$ 的 $ ext{LCA}$(也就是两条链的 $ ext{LCA}$ 中较深的那一个)。

    现在,我们对于给定的一条链 $(u, v)$,求出它的三个值:$lca, a, b$。$a$ 表示 $u$ 在 $lca$ 的哪一棵子树中,$b$ 表示 $v$ 在 $lca$ 的哪一棵子树中。或者可以理解为,设 $u$ 与 $lca$ 的深度差为 $k$,则 $a$ 是 $u$ 的 $k-1$ 级祖先,$b$ 是 $v$ 的 $k-1$ 级祖先。值得注意的是,可能 $u, v$ 互为祖先—后代关系,比如 $u$ 是 $v$ 的祖先,那么我们可以钦定 $a$ 为一个新开的点,避免对后面的统计造成影响。同时为了方便,请确保 $a<b$,这一点可以通过交换 $u, v$ 和 $a, b$ 很简单的达到。

    现在,我们分别考虑求出上面两种情况对答案的贡献。

    先考虑第一种情况,我们可以把所有的链先按 $lca$ 的深度,再按 $lca$ 排序。对于 $lca$ 相同的一些链,显然我们求的就是 $oldsymbol{a_x, b_x, a_y, b_y(a_x<a_y)}$ 互不相同的链对 $oldsymbol{(x, y)}$ 的数量。为了确保不重不漏,我们不妨按照 $a$ 严格递增的顺序去一段一段地枚举链 $y$,同时开个桶 $buk_i$ 记录一下从子树 $i$ 中伸出来的链的条数。每次 $ans gets ans + cnt - buk_{b_y}$,然后分别把 $buk_{a_y}, buk_{b_y} + 1$ 就好了。上面 $cnt$ 表示的是已经枚举过的链的条数。具体细节参考代码。

    考虑第二种情况,我们依然可以把所有的链先按 $lca$ 的深度,再按 $lca$ 排序。设以 $u$ 为根的子树对应 $ ext{DFS}$ 序上 $[ extit{ldf}_u, extit{rdf}_u]$ 这一段。用树状数组维护一个 $ ext{DFS}$ 序,每次 $ans gets ans + operatorname{Sum}( extit{ldf}_{lca}, extit{rdf}_{lca})$,然后如果 $a$ 不是新建的点的话,$ans gets ans - operatorname{Sum}( extit{ldf}_{a}, extit{rdf}_{a})$,$b$ 同理。最终我们把 $ ext{DFS}$ 序上的 $x$ 和 $y$ 位置分别 $+1$ 就可以了。正确性显然。

    按照上面的做法是一个 $log$ 的,但是通过一些奇技淫巧可以优化成线性,这里不再讨论。

    代码实现推荐兔队长的赛时提交,个人认为写的很清晰了。


  • 相关阅读:
    Arduino通信篇系列之print()和write()输出方式的差异
    通信协议之Modbus协议(一)
    CAD制图系列一之绘图、标注、修改、视图
    Arduino系列之pwm控制LED灯(呼吸灯)
    [转]GDB调试基础
    Linux上编辑然后执行一段脚本的机制
    [转]进程创建-终结流程图
    [ 转]Linux进程关系
    Linux进程管理(四、 进程终结)
    Linux进程管理(三、 线程)
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1486F.html
Copyright © 2020-2023  润新知