题目
给定一棵以 1 为根的树,每个节点有颜色。问以每个节点为根的子树中,出现次数最多的颜色的编号之和。(次数最多的可能有多个颜色)
分析
考虑暴力做
于是为了优化复杂度,使用树上启发式合并(静态链分治)
对于一个节点的子树,除重儿子外,遍历完都要清空数据
遍历完重儿子不清空数据,直接和父亲节点及其他子树数据合并算出父节点的答案
此时定义的重儿子就是子树大小
(Code)
#include<cstdio>
#define LL long long
using namespace std;
const int N = 1e5 + 5;
int n , tot , col[N] , h[N] , cnt[N] , siz[N] , son[N] , mx;
LL sum , ans[N];
struct edge{
int to , nxt;
}e[2 * N];
inline void add(int x , int y){e[++tot] = edge{y , h[x]} , h[x] = tot;}
void getson(int x , int fa)
{
siz[x] = 1;
for(register int i = h[x]; i; i = e[i].nxt)
{
if (e[i].to == fa) continue;
getson(e[i].to , x);
siz[x] += siz[e[i].to];
son[x] = siz[son[x]] < siz[e[i].to] ? e[i].to : son[x];
}
}
void update(int x , int fa , int v , int heavy)
{
cnt[col[x]] += v;
if (cnt[col[x]] > mx) mx = cnt[col[x]] , sum = col[x];
else if (cnt[col[x]] == mx) sum += col[x];
for(register int i = h[x]; i; i = e[i].nxt)
if (e[i].to != fa && e[i].to != heavy) update(e[i].to , x , v , heavy);
}
void calc(int x , int fa , int kp)
{
for(register int i = h[x]; i; i = e[i].nxt)
if (son[x] != e[i].to && fa != e[i].to) calc(e[i].to , x , 0);
if (son[x]) calc(son[x] , x , 1);
update(x , fa , 1 , son[x]) , ans[x] = sum;
if (!kp) update(x , fa , -1 , 0) , sum = mx = 0;
}
int main()
{
scanf("%d" , &n);
for(register int i = 1; i <= n; i++) scanf("%d" , col + i);
int x , y;
for(register int i = 1; i < n; i++)
scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
getson(1 , 0) , calc(1 , 0 , 1);
for(register int i = 1; i <= n; i++) printf("%lld " , ans[i]);
}