树--dsu on tree

dsu on tree

树上启发式合并

一般解决下列特征的题目:
1.询问子树的某些信息
2.没有修改操作

codeforces 600 E. Lomsat gelral

Problem Description

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

Analysis of ideas

首先将树进行轻重链剖分,处理出每个节点的重儿子

然后对于树上的询问,如果是重儿子,则暴力统计他的贡献,保留其贡献,轻儿子则在统计完他的贡献后消除其贡献

这里说的保留和消除是指的节点整个子树的贡献

举个栗子,上面这幅图中

我们先跳到2号节点,然后发现2号还有轻儿子,然后跳到5号节点,统计其信息,因为5号是轻儿子,然后消除它的贡献

然后2号还有重儿子...(dfs),之后到达12号节点,统计,消除

然后是11号节点,统计,保留,

回溯到6号节点,暴力统计它的轻儿子,也就是去搜6,12号节点,保留

回溯到2号节点,暴力统计它的轻儿子,也就是去搜2,5号节点,因为2是轻儿子,所以消除

重复这个过程

Accepted code

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
#define int long long
const int inf = 0x3f3f3f3f;
const int maxn = 101110;
const int M = 1e9+7;
int n,m,k,ok;

vector<int> v[maxn];        //图

int sz[maxn],son[maxn];     //i号节点子树的大小,i号节点的儿子

int col[maxn];      //i节点的颜色
int cnt[maxn];      //i号颜色的个数

int ans[maxn];

int sum,mx;         //最多编号颜色的和,最多编号个数

int Son;

void dfs(int u,int pre)     //轻重链剖分
{   
    sz[u] = 1;
    for(auto i : v[u])
    {
        if(i == pre) continue;
        dfs(i,u);
        sz[u] += sz[i];
        if(sz[i] > sz[son[u]]) son[u] = i;
    }
}

void add(int u,int pre,int val)     //给u的子树加上val
{
    cnt[col[u]]+=val;
    if(cnt[col[u]] > mx)
    {
        sum = col[u];
        mx = cnt[col[u]];
    }
    else if(cnt[col[u]] == mx)
    {
        sum += col[u];
    }

    for(auto i : v[u])
    {
        if(i == pre || i == Son) continue;
        add(i,u,val);
    }
}

//加的时候不考虑重儿子,删的时候考虑重儿子

void dfs2(int u,int pre,int opt)                    //opt=0表示结束后消除影响
{
    for(auto i : v[u])
    {
        if(i == pre || i == son[u]) continue;
        dfs2(i,u,0);                                //先搜轻儿子
    }
    if(son[u]) dfs2(son[u],u,1),Son = son[u];       //再搜重儿子,记录重儿子
    add(u,pre,1);                                   //暴力搜u的轻儿子
    Son = 0;                                        //接下来要删除,不考虑轻重儿子
    ans[u] = sum;
    if(!opt) add(u,pre,-1),mx = 0,sum = 0;          //消除影响
}

signed main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("data.in", "r", stdin);
#endif
    cin>>n;
    for(int i = 1; i <= n; i++) 
    {
        cin>>col[i];
    }
    for(int i = 1,x,y; i < n; i++) 
    {
        cin>>x>>y;
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1,0);
    dfs2(1,0,1);
    for(int i = 1; i <= n; i++) 
    {
        cout<<ans[i]<<' ';
    }
    cout<<endl;
    return 0;
}

参考博客

dsu on tree学习笔记

原文地址:https://www.cnblogs.com/hezongdnf/p/12585658.html