Lomsat gelral
题意:给你一个树,每个节点有一个颜色,求每个节点出现次数最大颜色编号和。
考虑每个点都必然有多颗子树,统计该点的答案时必然要合并多个子树。
启发式合并就是把小的往大的合并,这样保证log(n)的复杂度。
这个题考虑这样做法:
对于每一个点,
1 . 统计出所有轻儿子的答案,不计入当前点的答案,
2 . 统计重儿子的答案,计入当前点的答案,
3 . 再回头扫一遍轻儿子,将轻儿子的答案和重儿子的答案合并,最终得到当前点的答案。
这样合并保证了复杂度。
但对于每一个轻儿子,当处理完之后,就要删除贡献,因为会对其他轻儿子产生影响。
/* CF600E 启发式合并 复杂度 n*(log(n) */ #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int son[N],col[N],head[N],size[N]; long long cnt[N],ans[N]; struct edge{int v,next;}e[N<<2]; int ecnt,n,maxc,thisson; long long thissum; void init(){ memset(head,-1,sizeof head); memset(son,0,sizeof son); maxc=ecnt=thisson=thissum=0; } void add(int u,int v){ e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++; } void dfs_size(int u,int fa){//剖分树链 size[u]=1; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==fa)continue; dfs_size(v,u); size[u]+=size[v]; if(size[v]>size[son[u]])son[u]=v; } } void Count(int u,int fa,int val){//累积或删除贡献 cnt[col[u]]+=val; if(cnt[col[u]]>maxc){ maxc=cnt[col[u]]; thissum=col[u]; } else if(cnt[col[u]]==maxc)thissum+=col[u]; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==fa||v==thisson)continue; Count(v,u,val); } } void dfs_ans(int u,int fa,bool keep){//统计每个点的答案,保留或者不保留贡献 for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==fa||v==son[u])continue; dfs_ans(v,u,false); } if(son[u]){ dfs_ans(son[u],u,true); thisson=son[u]; } Count(u,fa,1); thisson=0; ans[u]=thissum; if(!keep){ Count(u,fa,-1); thissum=maxc=0; } } int main(){ scanf("%d",&n); init(); for(int i=1;i<=n;i++)scanf("%d",&col[i]); for(int i=1,u,v;i<n;i++){ scanf("%d %d",&u,&v); add(u,v);add(v,u); } dfs_size(1,-1); dfs_ans(1,-1,0); for(int i=1;i<=n;i++)printf("%I64d ",ans[i]); return 0; }