题目描述
给出一个树,树的每个节点有一个颜色。
询问每个节点颜色最多的颜色编号和。
解题思路
树上启发式合并?
考虑暴力,对于每个节点暴力遍历,之后删除该子树的贡献。
不然发现有些删除没有必要,贪心的思想,对于每棵子树保留重儿子的贡献。
所以操作为:遍历所有轻儿子不保留贡献,遍历重儿子保留贡献,遍历轻儿子增加贡献,得到该树答案。
时间复杂度:考虑每个节点遍历的次数,显然是到根节点的轻边个数加重链个数+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;
}