• ZROI 19.08.01 树上数据结构


    1.总览

    • LCT

    • 链分治(树剖)

    • 点/边分治

    2.点分治

    • 一棵树,点有(0/1),多次修改,询问最远的两个(1)距离。

    建出点分树,每个子树用堆维护:①最远的(1)距离;②它的每个儿子的①堆顶;

    全局维护每棵子树②堆最大的两个值,每次修改暴力改就可以。

    时间复杂度(O(n log^2n))(基本动态点分都是(log^2)以上的复杂度……然而仍然随便过(10^5),小常数可能性微存?)。


    • 一棵树,点有点权,每次询问距离一个点不超过(k)的点权和。

    每个点维护一个深度数据结构(这里树状数组就可以),询问的时候子树直接查,父亲递归上去。

    每个点对每个儿子单独维护一个树状数组,因为计算的时候要差分。


    • 一棵树,度数不超过(20),支持修改点权,求带权重心。

    考虑重心在树上转移的过程,如果不是到父亲,显然是走到(size_y>frac{size_x}{2})的一个子树。

    可以用点分树优化这个过程。

    每次扫一下所有儿子,找到最优的,直接跳到分治中心递归即可。

    由于重心是凸的,可以保证正确性。


    考虑查询的时候,在点分树上找到最浅的一个点(y),满足这个点被保留([l,r])后被(x)的连通块包含,则所有([l,r])间的点都在(y)的子树里。

    对于以(y)为分治中心的每个点(i),维护从(i)(y)路径上编号最大和最小的点(max_i,min_i)

    发现点(i)在连通块内的充要条件是([min_i,max_i] subseteq [l,r]),转化成了经典二维数点问题。

    3.链分治

    • (n)个点的树,每条边可以切断或不切断,求(1)号点连通块大小恰好为(k)的方案数,对ntt模数取模。(n leq 10^5)

    一个比较显然的暴力是直接背包,每对点会在(lca)处产生一次复杂度,总复杂度(O(n^2))

    观察dp式子(f_{x,j}=prod_{y in son_x}^{sum_k=j}(f_{y,k}+[k=0])),发现很像一个卷积。

    考虑链分治,树上轻儿子(size)和是(O(nlog n))级别。

    所以对于一条重链,可以先递归求出它链上所有轻儿子的生成函数,然后就变成了序列上问题,这个可以分治fft解决。总复杂度(O(nlog^3n)),不过常数很小。


    著名毒瘤题。(我写这题的时候splay和treap都被卡常了,最后换了压缩trie才过)

    比较naive的想法是根号分治+(log)数据结构。

    发现对于每个大点,修改次数均摊(O(sqrt n))次,询问次数只有均摊(O(1))次,用分块代替(log)数据结构,可以做到(O(nsqrt n))

    换一种思路,对每个点维护轻儿子,则不在这个集合里的只有重儿子,本身和父亲。修改的时候暴力跳重链就行了。

    单次修改(log^2n),询问(log n)。(实际上有一个(log)的做法)


    比较naive的想法是离线建出操作树,问题变成了求链上叉积最大值。然而直接维护凸包是(log^3 n)的(树剖+线段树/set+凸包上二分)。

    观察在重链上跳的过程,发现除了最后一次,每次都是重链上的一个前缀,那么可以离线询问,对每条重链直接暴力维护前缀凸包,两部分都是(log^2n)


    • (n)个点的树,(q)次操作,支持修改点权,询问最大权联通子图。(n, q leq 10^5)

    考虑序列上怎么做,发现就是一个经典的线段树题。

    把它搬到树上,发现就是一个动态dp,做完了。

    4.LCT

    • 动态dp:只要满足dp式子具有可合并性,即,两个操作复合之后可以变成同样形态,只是系数不同的操作,就可以用动态dp维护。

    • (n)个人,(0)~(n-1),每个人有一个(a_i)。初始有一个(x=0),按顺序依次执行每个人,可以选择把(x)变成((x+a_i) mod n),最后(x)获胜。每个人的策略都是:只有变了(x)必胜,且不变(x)不必胜时,他才会变。(q)次操作,每次修改某个(a_i),输出操作后谁会获胜。

    每轮游戏只会有一个人动手,且动手前后一定是(i-a_i => i)

    如果(igeq a_i),则(i)(i-a_i)连边,整个结构形成了一个森林。

    所有叶节点为(f_i=1),中间节点为(max(0, 1-sum_{jin son_i}f_j),lct维护动态dp即可。


    • (n)个点的点仙人掌,边带权,(m)次操作,每次修改一条边权或询问两点间最大流。

    最大流=最小割。

    最小割如果是环边,就一定会把环上的最小值删掉,不妨把它删掉并把它的权值加到环上的其他边上,lct维护即可。

    圆方树也可以做,合理设置边权即可。


    • (n)个点的树,(q)次操作:增加点权,翻转路径(权值),询问路径和/最大值/最小值。

    建两棵splay分别维护权值和形态,保证中序遍历一一对应,修改直接在权值splay上做就好了。


    发现修改操作和lct中的换根操作一模一样,直接lct维护就行了。需要一个树状数组来维护额外信息。


    • (A,B,C)三个点集,各有(n_A,n_B,n_C)个点。其中(Acup B,Acup C)分别构成两棵树,(A)内部没有边。随机(i,j),删除([B_1,B_i],[C_1,C_j]),问(A)仍然联通的概率。(n_ileq 10^5)

    枚举(i),显然(j)具有单调性,可以双指针。对于(AB)间的每条边,边权设为删除时间,加入(AC)边的时候删除(AB)间最早删除的边即可,lct随便维护。

    不知道为什么这种水题讲了那么久。

  • 相关阅读:
    C#:类的成员--属性
    C#:类的成员--字段
    C#:try语句
    solaris11 format zpool
    solaris硬盘格式化分区
    Linux使用pam_tally2.so模块限制登录失败锁定时间
    shell脚本if语句的多种条件参数
    系统重启后,mr程序不生成当前时间段的MRx文件问题
    hadoop学习笔记--找到执行hadoop的入口
    STREAMING HIVE流过滤 官网例子 注意中间用的py脚本
  • 原文地址:https://www.cnblogs.com/suwakow/p/11375070.html
Copyright © 2020-2023  润新知