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(' ');
}

 

posted @ 2019-08-14 20:08  Mr_Leceue  阅读(184)  评论(0编辑  收藏  举报