• 九省联考 2018 秘密袭击


    题意

    给定一颗含 $n$ 个结点的树,每个点有点权 $d_i$ ,求所有联通块中第 $k$ 大之和。

    $1leq n,m,kleq 1666,1leq d_ileq m$ ,时间限制 $5$ 秒。

    题解

    一道很有趣的题目。

    做法简述:由于 $dp$ 为卷积形式对其多项式求点值,并通过类似整体 $dp$ 的方式维护变换,再通过拉格朗日插值还原多项式。

    由于每次统计 $d_i$ 较难,一个直观的想法是转化成 $0/1$ 问题,若答案为 $d_i$ ,则在 $[1,d_i]$ 均统计一遍。

    具体的,答案可以写成 $sum_{S} kth(S)=sum_{S} sum_{i=1}^n [ileq kth(S)]=sum_{i=1}^n sum_S [ileq kth(S)]$ 。

    那么对于每个 $i$ ,将点权 $geq i$ 的设为 $1$ ,其余为 $0$ ,那么我们只需要知道有多少个联通块其点权和 $geq k$ 。

    这是一个简单的树形 $dp$ ,根据树形背包可得时间复杂度为 $mathcal O(nkw)$ 。

    据说就过了此题???喜提 $ ext{LOJ}$ 最优解,比我的快 $20$ 倍(

    我们将 $dp$ 式子写出,设 $f_{i,j,p}$ 表示当前根为 $i$ ,且考虑点权 $geq j$ 的情况时选择 $p$ 个 $1$ 的联通块个数。

    也很好转移
    $$
    f_{i,j,p}=prod_{(u,v)} (f_{v,j,c_v}+1) (sum c_v=p-[d_igeq j])
    $$
    由于背包的实质为卷积,不妨将该 $dp$ 写成 $ ext{OGF}$ 的形式。


    $$
    egin{aligned}
    F_{i,j}&=sum_{p=0}^n f_{i,j,p}cdot x^p\G_{i,j}&=sum_{vin sub(i)}^n f_{v,j}
    end{aligned}
    $$
    可得
    $$
    egin{aligned}
    F_{i,j}&=x^{[d_igeq j]}prod_{(u,v)} (F_{v,j}+1)\Ans&=sum_{i=1}^{m} sum_{j=k}^m [x^j] G_{1,i}\&=sum_{j=k}^m[x^j] sum_{i=1}^m G(1,i)
    end{aligned}
    $$
    显然这东西不能跑过去。

    考虑进一步的优化,由于多项式乘法太慢我们不妨求出 $sum_{i=1}^m G(1,i)$ 多项式的点值最后在通过拉格朗日插值插回去,那么我们需要求出 $n+1$ 个点值。

    显然
    $$
    egin{aligned}
    F_{i,j}(x)&=x^{[d_igeq j]} prod_{(u,v)} F_{v,j} (x)\G_{i,j} (x)&=sum_{(u,v)} G_{v,j} (x)+F_{i,j} (x)
    end{aligned}
    $$
    但这还是 $O(nkw)$ 的。(悲

    先考虑 $x$ 为定值且仅有一个的情况。

    我们需要维护的操作是维护 $n$ 个关于 $F,G$ 的点值数组。

    - 对于新点 $u$ ,$F$ 的前缀赋值为 $x$ ,后缀赋值为 $1$ 。$G$ 数组赋值为 $0$ 。
    - 对于 $(u,v)$ ,我们现将 $v$ 的 $F$数组全体加一,然后将两个 $F$ 数组对应位置相乘,将 $G$ 数组对应位置相加
    - 将 $u$ 的 $F$ 数组加到 $G$ 数组上。

    注意到这变换是相同的,不妨设计一个变换形式来实现快速转移。

    定义变换 $(a,b,c,d)$ 的作用为
    $$
    (a,b,c,d):(f,g)longrightarrow (af+b,cf+g+d)
    $$
    容易将上方三个式子写成相对应的变换,但我们要对于变换实现乘法
    $$
    (a_1,b_1,c_1,d_1)cdot (a_2,b_2,c_2,d_2)
    $$
    这个也很好推
    $$
    (a_1,b_1,c_1,d_1):(f,g)longrightarrow (a_1f+b_1,c_1f+g+d_1)\(a_2,b_2,c_2,d_2):(a_1f+b1,c_1f+g+d_1)longrightarrow (a_1a_2f+b_1a_2+b_2,(c_2a_1+c_1)f+g+c_2b_1+d_1+d_2)
    $$
    那么
    $$
    (a_1,b_1,c_1,d_1)cdot(a_2,b_2,c_2,d_2)=(a_1a_2,b_1a_2+b_2,c_2a_1+c_1,c_2b_1+d_1+d_2)
    $$
    这样我们就可以知道每个点所对应的变换来快速求出答案了。

    但这还是 $mathcal O(nkw)$ 的。(悲

    注意到上述操作很像区间加法,不妨用线段树支持变换。

    具体来讲,我们类似打区间 $tag$ ,标记下传的方式可以得到每个点的变换,而对于合并子树也类似线段树合并。

    而这个线段树合并需要支持标记下传,并且由于变换的乘法没有交换律我们需要考虑一下非空的子结点是 $u/v$ 。

    这个线段树合并的时/空复杂度也是 $mathcal O(nlog n)$ 的,因为标记下传仅在两个线段树的 $cap$ 下传,时间复杂度依然是 $mathcal O(nlog n)$ 的,所以空间复杂度仅多了常数 $(2)$ 倍。

    由于我们需要求 $x$ 在 $1sim n+1$ 的点值需要将上述做法重复 $n+1$ 遍。

    即总时间复杂度为 $mathcal O(n^2log n)$ ,但这常数肉眼可见的大,所以被暴力碾了。

    https://loj.ac/s/1084408

  • 相关阅读:
    3、Semantic-UI之定义容器
    2、Semantic-UI之网格布局
    1、Semantic-UI之开发环境搭建
    PyCharm创建普通项目配置支持jinja2语法
    CentOS 7安装GitLab、汉化、配置邮件发送
    Docker swarm 实战-部署wordpress
    线程和进程
    NGINX内置变量
    iTerm2 + Oh My Zsh 打造舒适终端体验
    CentOS 7修改网卡名称
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/14495406.html
Copyright © 2020-2023  润新知