dsu on tree(树上启发式合并)+例题(Lomsat gelral CodeForces-600E)


 

简介:

对于需要用树上启发式合并的题有两个特征:

1、只有对子树的询问。

2、没有修改。

此时很可能就是树上启发式合并了。

 


算法过程:

考虑暴力算法:就是对每一个节点的子树中的每个点对这棵子树贡献统计出来更新答案,然后消除贡献,继续递归求另一个子树的答案。复杂度 O(n^2) 

前置知识:轻重链剖分:对于树上的每一个点,与其相连的边中,连向子树的节点数量最多的称为重边,其余的边都为轻边。

 

dsu on tree 过程:

对于节点 i 

1、遍历每个节点。

2、递归解决每个轻儿子答案,然后消除递归产生的影响。

3、对于每个重儿子,不消除递归的影响。

4、统计每个轻儿子对答案的影响。

5、更新该节点的答案。

6、删除每个轻儿子的对答案的影响。


复杂度分析:

复杂度: O(𝑛𝑙𝑜𝑔𝑛) 。

性质:一个节点到根的路径上轻边个数不会超过 𝑙𝑜𝑔𝑛 条。


 

模版题:

Lomsat gelral CodeForces-600E:

题意:给你一棵树,树上每个节点都有对应的颜色,求出每个节点的子树中,出现次数最多的颜色的编号之和。

代码:

//#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define ll long long
#define MOD 1000000007
#define pdd pair<double,double>
#define mem(a,x) memset(a,x,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
const long long INF = 1e18+5;
const int inf = 0x3f3f3f3f;  
const double eps=1e-6;       
const int maxn=100005;       
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(int &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}

int son[maxn],siz[maxn],col[maxn],cnt[maxn],Mx,Son,N;
ll sum=0,ans[maxn];
vector<int>g[maxn];

//轻重链部分
void dfs(int x,int fa){
    siz[x]=1;
    for(int i=0;i<g[x].size();i++){
        int to=g[x][i];
        if(to==fa)continue;
        dfs(to,x);
        siz[x]+=siz[to];
        if(siz[to]>siz[son[x]])son[x]=to;
    }
}

void add(int x,int fa,int val)
{
    cnt[col[x]]+=val;
    if(cnt[col[x]]>Mx){
        Mx=cnt[col[x]];
        sum=col[x];
    }else if(cnt[col[x]]==Mx){
        sum+=col[x];
    }
    for(int i=0;i<g[x].size();i++){
        int to=g[x][i];
        if(to==fa||to==Son)continue;
        add(to,x,val);
    }
}

//启发式合并
void dfs2(int x,int fa,int opt)
{
    for(int i=0;i<g[x].size();i++){
        int to=g[x][i];
        if(to==fa)continue;
        if(to!=son[x]){
            dfs2(to,x,0);
        }
    }
    if(son[x]){
        dfs2(son[x],x,1);
        Son=son[x];
    }
    add(x,fa,1);
    Son=0;
    ans[x]=sum;
    if(!opt){
        add(x,fa,-1);
        Mx=0;
        sum=0;
    }
}

int main()
{
    cin>>N;
    for(int i=1;i<=N;i++){
        scanf("%d",&col[i]);
    }
    for(int i=1;i<N;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1,0);
    dfs2(1,0,0);
    for(int i=1;i<=N;i++){
        printf("%lld%c",ans[i]," \n"[i==N]);
    }
}

 

posted @ 2020-11-11 20:01  hachuochuo  阅读(139)  评论(0编辑  收藏  举报