题目大意:求每个点的子树中颜色的出现数量最多的颜色的和。
题解:dsu on the tree
树上启发式合并。其实是一个很暴力的做法,用来解决统计树上一个节点的子树中具有某种特征的节点数,或者子树中的某个值。
做法非常的暴力,就是如果是轻儿子,每次统计完了就将影响消除,如果是重儿子就保留。每次统计答案的时候,先计算轻儿子,在计算中儿子。
最后均摊的时间复杂度是O(nlogn),时间复杂度的证明我也不太会,想明白了再来写。。。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 200003
#define LL long long
using namespace std;
int n,m,point[N],v[N],nxt[N],mark[N];
int size[N],son[N],tot;
LL cx,sum,num[N],ans[N],col[N];
void add(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void solve(int x,int f)
{
size[x]=1;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==f) continue;
solve(v[i],x);
size[x]+=size[v[i]];
if (size[v[i]]>size[son[x]]) son[x]=v[i];
}
}
void calc(int x,int f,int val)
{
num[col[x]]+=(LL)val;
if (val>0&&num[col[x]]>=cx) {
if (num[col[x]]>cx) sum=0,cx=num[col[x]];
sum+=(LL)col[x];
}
for (int i=point[x];i;i=nxt[i])
if (v[i]!=f&&!mark[v[i]]) calc(v[i],x,val);
}
void dfs(int x,int f,bool k=0)
{
for (int i=point[x];i;i=nxt[i])
if (v[i]!=f&&v[i]!=son[x]) dfs(v[i],x,0);
if (son[x]) dfs(son[x],x,1),mark[son[x]]=1;
calc(x,f,1);
ans[x]=sum;
if (son[x]) mark[son[x]]=0;
if (!k) calc(x,f,-1),cx=sum=0;
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%I64d",&col[i]);
for (int i=1;i<n;i++) {
int x,y; scanf("%d%d",&x,&y);
add(x,y);
}
solve(1,0);
dfs(1,0,0);
for (int i=1;i<=n;i++) printf("%I64d ",ans[i]);
printf("\n");
}