• dsu on tree ——附带buff的暴力解法


    这篇博客只是简单叙述思想(因为ML太弱了),具体例题请转其他博客.


    dsu on tree,许多OI将其归于启发式合并,当然如果你能理解更好,这只是一个理解方式罢了.

    思想简述

      顾名思义,这个算法是处理树上问题,将子树分开求解,如果暴力了话是枚举每个子树,然后dfs;

      这里将每次dfs完的清空操作重新定义,并且规定dfs顺序,以来优化成log解法.

      这里我们引入一个 例题

    一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。

      仔细读题我们可以先想到暴力解法,分别从一个点进行dfs,肯定会爆;

      那么我们思考一下,一颗子树上的点dfs结果是否可以被该子树根节点利用?

      当然可以,但是我们只能保留一个点的结果,所以我们就面临一个选择,留哪一个?

      我们想到树剖,那么我们保留重儿子就可以较好的保留信息,时间复杂度也会大幅降低.

    操作步骤

      1.dfs1找到重儿子和轻儿子;

      2.dfs2递归处理每一个点,先扫描轻儿子,再扫描重儿子.

      3.设计calc函数,用来处理轻重儿子,可以将add和del分开写,也可以合在一起;

    代码实现

      

    #include<bits/stdc++.h>
    #define maxn 100007
    #define ll long long
    using namespace std;
    int n,head[maxn],a[maxn],sz[maxn],son[maxn],cent;
    int fa[maxn],vis[maxn];
    ll col,max_part,num[maxn],ans[maxn];
    struct node{
        int next,to;
    }edge[maxn<<2];
    
    template<typename type_of_scan>
    inline void scan(type_of_scan &x){
        type_of_scan f=1;x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    template<typename type_of_print>
    inline void print(type_of_print x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    
    inline void add(int u,int v){
        edge[++cent]=(node){head[u],v};head[u]=cent;
        edge[++cent]=(node){head[v],u};head[v]=cent;
    }
    
    void dfs1(int x){
        sz[x]=1;
        for(int i=head[x];i;i=edge[i].next){
            int y=edge[i].to;
            if(y==fa[x]) continue;
            fa[y]=x;dfs1(y);sz[x]+=sz[y];
            if(sz[son[x]]<sz[y]) son[x]=y;
        }
    }
    
    void calc(int x,int p){
        num[a[x]]+=p;
        if(p>0&&num[a[x]]==max_part) col+=a[x];
        if(p>0&&num[a[x]]>max_part) col=a[x],max_part=num[a[x]];
        for(int i=head[x];i;i=edge[i].next){
            int y=edge[i].to;
            if(y==fa[x]||vis[y]) continue;
            calc(y,p);
        }
    }
    
    void dfs2(int x,int id){
        for(int i=head[x];i;i=edge[i].next){
            int y=edge[i].to;
            if(y==fa[x]||y==son[x]) continue;
            dfs2(y,0);
        }
        if(son[x]) dfs2(son[x],1),vis[son[x]]=1;
        calc(x,1);ans[x]=col;
        if(son[x]) vis[son[x]]=0;
        if(!id) calc(x,-1),max_part=col=0;
    }
    
    int main(){
        scan(n);
        for(int i=1;i<=n;i++) scan(a[i]);
        for(int i=1,u,v;i<=n-1;i++)
            scan(u),scan(v),add(u,v);
        dfs1(1),dfs2(1,1);
        for(int i=1;i<=n;i++) print(ans[i]),putchar(' ');
    }
  • 相关阅读:
    网页中15秒后重新发送验证码,多少秒后发送验证码
    javascript 数组去重 unique
    微信支付和微信支付通知基于sdk的说明
    php对象序列化和cookie的问题,反序列化false
    php对象序列化总出错false
    图片懒加载lazyload.js详解
    手机联动地址选择框,移动端地址联动
    Java类加载原理解析(转)
    location对象介绍
    SpringMVC使用fastjson自定义Converter支持返回jsonp格式(转)
  • 原文地址:https://www.cnblogs.com/waterflower/p/11354392.html
Copyright © 2020-2023  润新知