zoukankan      html  css  js  c++  java
  • 洛谷 P5291 [十二省联考2019]希望

    洛谷 P5291 [十二省联考2019]希望

    https://www.luogu.com.cn/problem/P5291

    https://www.luogu.com.cn/problemnew/solution/P5291

    一个有 (n) 个节点的树,一共有 (k) 个救援队,每个救援队有一个救援范围,即一个连通块 (s_i)

    我们称一个节点 (u) 可被第 (i) 个救援队到达当且仅当

    1. (u in s_i)
    2. (forall v in s_i, dis(u, v) le L)

    求有多少方案 ({ s_1, cdots, s_k }) 满足存在一个节点可被所有救援队到达

    两种方案不同当且仅当存在 (i) , 使得 (s_i ot= s'_i)

    答案对 (998244353) 取模

    (1 le n le 10^6)

    (0 le L le n)

    (1 le k le 10)

    Tutorial

    由于对于一种合法的方案,可以选择的点构成了一个连通块,我们可以利用树上 点数-边数=1 这一特点,将对于每个连通块统计答案转化为对于每个点和边统计答案.

    (f(u, i)) 表示 (u) 的子树中包含 (u) 且所有点距离 (u) 不超过 (i) 的连通块个数 +1

    [f(u, i) = 1 + prod_{v in son(u)} f(v, i - 1) ]

    (g(u, i)) 表示包含 (u) 且不包含 (u) 子树中的点且所有点距离 (u) 不超过 (i) 的连通块个数

    [g(u, i) = 1 + g(fa, i - 1) prod_{v in son(fa), v ot = u} f(v, i - 2) ]

    每个点的贡献为

    [((f(u, L) - 1)g(u, L))^k ]

    每条边的贡献为

    [((f(u, L - 1) - 1)(g(u, L) - 1))^k ]

    直接DP的复杂度为 (O(nL)) ,接下来我们考虑如何优化计算 (f,g) 的过程

    由于第二维与深度有关,所以考虑使用长链剖分优化.

    (f) 的优化就是经典问题 , 需要进行所有值 +1 的操作,维护一个加法标记.

    对于 (g) 我们需要从上到下更新,对于 (u) 节点,我们需要知道的只有 (g(u, L)) ,所以我们只需要保留 $g(u, L - len(u) + 1) $ 到 (g(u, L)) 的DP值,其中 (len(u)) 表示 (u) 出发的长链的点数.

    对于上边 (g) 的转移中的 (prod) 的部分,其实也就是 (dfrac {f(fa, i - 1) - 1}{f(v, i - 2)}) 但是 (f(v, i - 2)) 可能等于 (0) ,我们可以看作 (f(v, i - 2)) 序列的一个前缀和一个后缀的乘积.

    一个前缀的积可以表示为在计算 (f) 的过程中某一时刻的 (f(fa, i - 1)) ,为了得到这个值,我们可以在计算 (f) 每次转移时纪录修改过的值,就可以实现回溯.

    一个后缀的积,可以在求 (g) 的时候采用与计算 (f) 时相反的顺序,类似维护 (f) 的方法维护.

    注意由于 (f,g) 的定义中都有 不超过 ,以 (f) 为例, 设 (l_u = len(u) - 1),实际上 (f(u, k), k > l_u) 也是有值的,且等于 (f(u, l_u)) , 因此转移时 (f(u)) 的一段后缀会乘上 (f(v, l_v))

    (f(v, l_v) = 0) 时相当于将一段后缀赋值,可以维护 (pos) 表示 (f(v, k), k ge pos) 的值都等于 (0)

    (f(v, l_v) ot=0) 时,由于后缀之外的部分是 (O(l_v)) 的,所以我们可以将那段前缀乘上 (f(v, l_v)) 的逆元,然后维护一个乘法标记.

    由于 (n le 10^6) ,所以我们想办法去掉求逆元带来的 (O(log n)) , 发现我们只需要求出所有 (f(u, l_u))(u) 子树中所有包含 (u) 的连通块的个数, 可以用类似求阶乘逆元的方法求出这 (O(n)) 个数的逆元.

    总时间复杂度 (O(n))

    Summary

    首先利用 点数-边数=1 将统计连通块转化为统计点.

    写出DP方程后,发现第二维与深度有关,于是考虑长链剖分.

    由于只关心 (g(u, L)) , 所以从上到下的部分也可以使用长链剖分

    通过纪录每次修改的位置,来实现回溯.

    对于全局加,后缀乘,后缀赋值,利用标记在复杂度不劣化的条件下维护.

    (n) 个数逆元可以用类似求阶乘逆元的方法做到 (O(n))

  • 相关阅读:
    vim编辑器入门
    线程概念
    forkJoin
    join()方法跟踪
    mybatis 注解和xml 优缺点
    sql 索引 sql_safe_updates
    spirngcloud文件
    springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
    创建线程池的四种方式
    ThreadLocal
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13193020.html
Copyright © 2011-2022 走看看