[Dsu on tree]CodeForces 600 E

题目描述

给出一个树,树的每个节点有一个颜色。

询问每个节点颜色最多的颜色编号和。

解题思路

树上启发式合并?

考虑暴力,对于每个节点暴力遍历,之后删除该子树的贡献。

不然发现有些删除没有必要,贪心的思想,对于每棵子树保留重儿子的贡献。

所以操作为:遍历所有轻儿子不保留贡献,遍历重儿子保留贡献,遍历轻儿子增加贡献,得到该树答案。

时间复杂度:考虑每个节点遍历的次数,显然是到根节点的轻边个数加重链个数+1,所以效率就是 O(nlog2N)

#include<cstdio>
#define LL long long
using namespace std;
const int maxn=100005;
int tot,lnk[maxn],son[2*maxn],nxt[2*maxn];
int n,c[maxn],s[maxn],h[maxn],MX,w[maxn];
LL ans[maxn],sum;
inline int _read(){
    int num=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
void add(int x,int y){nxt[++tot]=lnk[x];lnk[x]=tot;son[tot]=y;}
void DFS1(int x){
    s[x]=1;
    for (int j=lnk[x];j;j=nxt[j]) if (!s[son[j]]){
        DFS1(son[j]);s[x]+=s[son[j]];
        if (s[son[j]]>s[h[x]]) h[x]=son[j];
    }
}
void change(int x,int fa,int hx,int k){
    w[c[x]]+=k;
    if (w[c[x]]==MX) sum+=c[x];
    if (w[c[x]]>MX) MX=w[c[x]],sum=c[x];
    for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa&&son[j]!=hx) change(son[j],x,hx,k);
}
void DFS2(int x,int fa,int ues){
    for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa&&son[j]!=h[x]) DFS2(son[j],x,0);
    if (h[x]) DFS2(h[x],x,1);
    change(x,fa,h[x],1);ans[x]=sum;
    if (!ues) change(x,fa,0,-1),sum=MX=0;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();
    for (int i=1;i<=n;i++) c[i]=_read();
    for (int i=1;i<n;i++){
        int x=_read(),y=_read();
        add(x,y);add(y,x);
    }
    DFS1(1);DFS2(1,0,1);
    for (int i=1;i<=n;i++) printf("%lld ",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值