CF 600E 树上众数 启发式合并

本文详细探讨了在树结构中如何高效地处理众数查询以及在每次元素增减1时如何维护数组的最大值。通过对问题的深入分析,提出了一种启发式合并的解决方案。

顺便实现了在每次增减1的情况下,数组最大值的维护。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct edge{
    int to,next;
}e[200003];
int head[100003];
int cnt;
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void add(int u,int v){
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
int mc[100003];
int c[100003];
int n;
ll ans[100003];
int siz[100003];
int tim[100003];
ll grp[100003];
int mx;
ll sum;
void dfs(int u,int fa){
    siz[u]=1;
    mc[u]=-1;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        siz[u]+=siz[v];
        if(mc[u]==-1||siz[mc[u]]<siz[v])mc[u]=v;
    }
}
void add(int u){
    int p=++tim[c[u]];
    if(p==1){
        grp[1]+=c[u];
    }
    else {
        grp[p]+=c[u];
        grp[p-1]-=c[u];
    }
    if(p>=mx){
        mx=p;
    }
}
void del(int u){
    int p=--tim[c[u]];
    if(p==0){
        grp[1]-=c[u];
    }
    else {
        grp[p]+=c[u];
        grp[p+1]-=c[u];
    }
    if(!grp[mx]){
        mx--;
    }
}
void addtree(int u,int fa){
    add(u);
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        addtree(v,u);
    }
}
void deltree(int u,int fa){
    del(u);
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        deltree(v,u);
    }
}
void dfs2(int u,int fa){
    if(mc[u]==-1){
        add(u);
        ans[u]=grp[mx];
        return;
    }
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa||v==mc[u])continue;
        dfs2(v,u);
        deltree(v,u);
    }
    dfs2(mc[u],u);
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==fa||v==mc[u])continue;
        addtree(v,u);
    }
    add(u);
    ans[u]=grp[mx];
    return ;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&c[i]);
    }
    init();
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs(1,0);
    mx=0;
    sum=0;
    dfs2(1,0);
    for(int i=1;i<=n;i++)printf("%lld%c",ans[i],i==n?'\n':' ');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值