http://codeforces.com/problemset/problem/600/E
树上启发式合并就是暴力 但是利用dfs序的特点 把最大子树放在最后 递归返回时可以省去更新该子树 因为一个点到祖先的路径中 轻边的数量是logn左右的 所以一个点最多被更新logn次 从而把时间降到nlogn
详细请参考https://blog.csdn.net/ZJZNKU/article/details/65937416
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
struct node
{
int v,next;
};
node edge[2*maxn];
ll ans[maxn];
ll tmp;
int clr[maxn],first[maxn],sum[maxn],son[maxn],mp1[maxn],mp2[maxn],book[maxn];
int n,num,maxx;
void addedge(int u,int v)
{
edge[num].v=v;
edge[num].next=first[u];
first[u]=num++;
}
void dfsI(int cur,int fa)
{
int i,v;
sum[cur]=1,son[cur]=-1;
num++;
mp1[cur]=num,mp2[num]=cur;
for(i=first[cur];i!=-1;i=edge[i].next){
v=edge[i].v;
if(v!=fa){
dfsI(v,cur);
sum[cur]+=sum[v];
if(son[cur]==-1||sum[son[cur]]<sum[v]) son[cur]=v;
}
}
}
void dfsII(int cur,int fa,int flag)
{
int i,j,v;
for(i=first[cur];i!=-1;i=edge[i].next){
v=edge[i].v;
if(v!=fa&&v!=son[cur]){
dfsII(v,cur,0);
}
}
if(son[cur]!=-1) dfsII(son[cur],cur,1);
book[clr[cur]]++;
if(maxx==book[clr[cur]]) tmp+=(ll)(clr[cur]);
else if(maxx<book[clr[cur]]) maxx=book[clr[cur]],tmp=clr[cur];
for(i=first[cur];i!=-1;i=edge[i].next){
v=edge[i].v;
if(v!=fa&&v!=son[cur]){
for(j=mp1[v];j<=mp1[v]+sum[v]-1;j++){
book[clr[mp2[j]]]++;
if(maxx==book[clr[mp2[j]]]) tmp+=(ll)(clr[mp2[j]]);
else if(maxx<book[clr[mp2[j]]]) maxx=book[clr[mp2[j]]],tmp=clr[mp2[j]];
}
}
}
ans[cur]=tmp;
if(!flag){
for(j=mp1[cur];j<=mp1[cur]+sum[cur]-1;j++) book[clr[mp2[j]]]--;
maxx=0,tmp=0;
}
}
int main()
{
int i,u,v;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&clr[i]);
memset(first,-1,sizeof(first));
num=0;
for(i=1;i<=n-1;i++){
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
num=0;
dfsI(1,0);
dfsII(1,0,0);
for(i=1;i<=n;i++) printf("%lld ",ans[i]);
printf("\n");
return 0;
}