zoukankan      html  css  js  c++  java
  • 树上启发式合并转载

    前言

    某一天发现一道树上启发式合并裸题,但我不会写……
    学习并刷了两天的题,是时候来写个总结了

    正文

    树上启发式合并(DSU on Tree),是一个在 O ( n l o g n ) O(nlogn) O(nlogn)时间内解决许多树上问题的有力算法。
    但它的中心其实是——暴力!
    没错,它正是由暴力优化而来。
    我们先看一道例题:CF600E Lomsat gelral
    题意简述:一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。
    我们先思考暴力:对于每个节点,暴力遍历子树,将它们的数据统计出来得到当前节点的答案,然后再暴力将这棵子树的数据清空,以免影响到别的节点。
    很明显,这个做法是 O ( n 2 ) O(n^2) O(n2)的。

    有的同学可能会有疑问:为什么要清空呢?
       \;
    由于空间的限制,我们不可能对于每一个节点开一个数组来记录数据,只能开一个全局数组。
    在这个全局数组内,如果不清空,就会影响到别的子树,于是导致答案错误。
    然而可以发现,统计儿子节点时最后那个节点其实没有必要清空,因为它不再会影响到它的兄弟节点。
    这也正是接下来要讲到的优化方法。

    思考优化:对于节点x,可以在做子树答案时保留最后一棵子树v的数据不清空,然后统计x的答案时绕过v节点统计别的子树。那么v选哪个呢?当然是选size最大的。
    于是,我们得到了一个优化后的做法:对于节点x,先统计轻儿子的答案,并将它们的数据清除;然后统计重儿子的答案,保留数据;最后遍历其他轻儿子及其子树,把它们的数据与重儿子合并。
    非常神奇的是,经过分析,可以证明它的复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)的!(然而我不会证明,也懒得学)
    回到例题,这正是可以用这种方法简单解决的。放代码:

    bool s[sz];//是否是重儿子
    int cnt[sz];//每种颜色出现次数
    ll sum[sz],top;//每个次数之和,以及最多的次数
    void add(int c,int t)
    {
        sum[cnt[c]]-=c;//原来的减去
        cnt[c]+=t;
        sum[cnt[c]]+=c;//新的加上
        if (sum[top+1]) ++top;//更新最大值
        if (!sum[top]) --top;
    }
    void add(int x,int fa,int t)
    {
        add(col[x],t);//更新数据
        go(x) if (v!=fa&&!s[v]/*绕过重儿子*/) add(v,x,t);
    }
    ll ans[sz];
    void dfs(int x,int fa,bool keep)//keep:是否保留当前子树的数据
    {
        go(x) if (v!=fa&&v!=son[x]) dfs(v,x,0);//遍历轻儿子,不保留数据
        if (son[x]) dfs(son[x],x,1);//遍历重儿子,保留数据
        s[son[x]]=1;//标记重儿子
        add(x,fa,1);//把轻儿子与重儿子的数据合并
        ans[x]=sum[top];
        s[son[x]]=0;//取消标记
        if (!keep) add(x,fa,-1);//若不保留则暴力删除数据(相当于memset,但memset太慢)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    练习

    经过上面的讲解,相信各位已经大概明白了树上启发式合并的思路。接下来还有几道练习题(地址均为洛谷题库,要去原OJ请通过洛谷上的链接过去):
    CF570D Tree Requests
    CF208E Blood Cousins
    CF246E Blood Cousins Return
    CF1009F Dominant Indices
    CF375D Tree and Queries
    CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
    洛谷上均有对应的树上启发式合并题解,有一些是我的,可以点赞。(不要脸地骗个赞)
    如果还不太明白,可以去这个博客看。(然而是英文的)

    完结撒花!!

  • 相关阅读:
    @Transactional 什么情况下会失效?
    如何主持一场专业的面试?
    MIT-HIB 心率数据库及相关
    hadoop中Writable类
    XXX.jar has no source attachment
    Win10Eclipse配置个人本地hadoop
    js去除两个数组中重复的元素
    JS找出两个数组中不相同的元素
    flex中order控制元素的排列顺序
    flex中align-self设置侧轴的某元素的对其方式
  • 原文地址:https://www.cnblogs.com/CHK666/p/15577145.html
Copyright © 2011-2022 走看看