【dsu on tree】Codeforces600E[Lomsat gelral]题解

题目概述

给出一棵 n 个节点的树,每个节点有一个颜色。如果 c 是一个节点子树中出现次数最多的颜色,则称该节点被 c 占领(一个节点可以被许多颜色占领)。求每个节点的 c

解题报告

烂大街的dsu on tree(树上启发式合并)经典题。先树剖,对于 x 我们递归求出轻儿子的答案(不记录信息),然后再求出重儿子的答案(记录信息)。之后暴力统计所有子树节点的信息(重儿子为根的子树不需要再统计)求出 x 的答案,若 x 不需要保留信息,则再将 x 子树的信息暴力抹去。

因为重儿子留着不删,所以就是父亲以及轻儿子合并到重儿子的过程。由于所在子树大小至少 ×2 ,所以显然每个节点只会被插入 log2n 次,效率 O(nlog2n)

示例程序

#include<cstdio>
using namespace std;
typedef long long LL;
const int maxn=100000,maxe=maxn<<1;

int n,col[maxn+5],fa[maxn+5];
int E,lnk[maxn+5],son[maxe+5],nxt[maxe+5];
int si[maxn+5],SH[maxn+5];bool vis[maxn+5];
int MAX,num[maxn+5];LL sum,ans[maxn+5];

#define Add(x,y) son[++E]=y,nxt[E]=lnk[x],lnk[x]=E
void Dfs(int x,int pre=0)
{
    si[x]=1;fa[x]=pre;
    for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=pre)
    {
        Dfs(son[j],x);si[x]+=si[son[j]];
        if (si[son[j]]>si[SH[x]]) SH[x]=son[j];
    }
}
void Update(int x,int f)
{
    if ((num[col[x]]+=f)>=MAX)  if (num[col[x]]>MAX)
        MAX=num[col[x]],sum=col[x]; else sum+=col[x];
    for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa[x])
        if (!vis[son[j]]) Update(son[j],f);
}
void Solve(int x,bool fl=true)
{
    for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa[x])
        if (son[j]!=SH[x]) Solve(son[j],false);
    if (SH[x]) Solve(SH[x],true),vis[SH[x]]=true;
    Update(x,1);ans[x]=sum;if (SH[x]) vis[SH[x]]=false;
    if (!fl) Update(x,-1),MAX=sum=0;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&col[i]);
    for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),Add(x,y),Add(y,x);
    Dfs(1);Solve(1);for (int i=1;i<=n;i++) printf("%I64d ",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值