(复习)树上启发式合并(dsu on tree)入门U41492树上数颜色

主要思想是树的重轻儿子之分使得时间复杂度为o(nlogn),神奇
欲深入了解的这里:https://oi-wiki.org/graph/dsu-on-tree/

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef struct edge//边结构体
{
    int to,next;
}EDGE;
//边相关数组
EDGE e[100001<<1];
int head[100001];
int cnt;
//树大小及DFS序相关数组
int son[100001];//根节点为i的重儿子编号
int big[100001];//树的大小
int b[100001];//树节点的起始DFS序
int ed[100001];//树节点的最远DFS序
int match[100001];//DFS序对应的节点编号
int pos;//DFS序数
//颜色,答案相关数组
int c[100001];//节点颜色
int colornumber;//现在有的颜色数量
int times[100001];//每个颜色出现的次数
int ans[100001];//节点x的答案
void add(int x,int y)//建立边
{
    e[++cnt].to = y;
    e[cnt].next = head[x];
    head[x] = cnt;
}
void add_color(int x)//加颜色
{
    times[c[x]]++;
    if(times[c[x]] == 1) colornumber++;
}
void del_color(int x)//删颜色
{
    times[c[x]]--;
    if(times[c[x]] == 0) colornumber--;
}
void DFS1(int x,int fa)//预处理DFS
{
    big[x] = 1;//初始化树大小
    match[++pos] = x;//DFS序匹配
    b[x] = pos;//节点的DFS序
    for (int i = head[x];i;i = e[i].next)//DFS遍历
    {
        if(e[i].to == fa) continue;
        DFS1(e[i].to,x);
        big[x] += big[e[i].to];//累加大小
        if(son[x] == 0||big[e[i].to] > big[son[x]]) son[x] = e[i].to;//更新重儿子
    }
    ed[x] = pos;//记录最远DFS序
}
void DFS2(int x,int fa,bool keep)//树上启发式合并(dus on tree),keep为是否计算完要删除
{
    for (int i = head[x];i;i = e[i].next)//先计算所有轻儿子的树答案
    {
        if(e[i].to == fa||e[i].to == son[x]) continue;//注意条件
        DFS2(e[i].to,x,false);//继承删除
    }
    if(son[x]) DFS2(son[x],x,true);//有重儿子的话计算,并先不删除
    for (int i = head[x];i;i = e[i].next)//因为之前的轻儿子已经删除,要重新计算
    {
        if(e[i].to == fa||e[i].to == son[x]) continue;
        //由于儿子的DFS序连续,直接循环,若递归代码非常复杂!!!
        for (int j = b[e[i].to];j<=ed[e[i].to];j++) add_color(match[j]);//注意是DFS序的配对节点
    }
    add_color(x);//再加上自己
    ans[x] = colornumber;//记录答案
    if(!keep)//如果要删除
    {
        for (int i = b[x];i<=ed[x];i++) del_color(match[i]);//从自己开始删
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n,m,x,y;
    cin>>n;
    for (int i = 1;i<=n-1;i++)//建树
    {
        cin>>x>>y;
        add(x,y);
        add(y,x);
    }
    for (int i = 1;i<=n;i++) cin>>c[i];//输入颜色
    DFS1(1,0);//预处理
    DFS2(1,0,false);//dus on tree
    cin>>m;
    while(m--)//回答m次询问
    {
        cin>>x;
        cout<<ans[x]<<"\n";//o(1)回答
    }
    return 0;
}

码字不易,多多支持
posted @ 2024-04-21 21:05  游辰  阅读(19)  评论(0编辑  收藏  举报