CF600E Lomsat gelral

题目

给定一棵以 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]);
}
原文地址:https://www.cnblogs.com/leiyuanze/p/13913937.html