题意
给出一颗树,求每个子树中最多的颜色的编号的总和。
思路
题目有点拗口,其实就是求众数的和。
利用权值线段树维护区间答案以及众数出现的次数。枚举当前点的每个儿子,把儿子们上线段树的信息合并。
线段树合并的时空复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),所以 1 e 5 1e5 1e5能过掉。
思路
#include <cstdio>
#include <algorithm>
struct SegmentTree {
int l, r, maxx;
long long ans;
}t[2000001];
int n, tot, cnt;
int a[100001], ver[200001], head[100001], next[200001], root[200001];
long long ans[100001];
void add(int u, int v) {
ver[++tot] = v;
next[tot] = head[u];
head[u] = tot;
}
void spread(int a) {
if (t[t[a].l].maxx > t[t[a].r].maxx) {
t[a].maxx = t[t[a].l].maxx;
t[a].ans = t[t[a].l].ans;
} else if (t[t[a].l].maxx < t[t[a].r].maxx) {
t[a].maxx = t[t[a].r].maxx;
t[a].ans = t[t[a].r].ans;
} else {
t[a].maxx = t[t[a].l].maxx;
t[a].ans = t[t[a].l].ans + t[t[a].r].ans;
}
}
int merge(int r1, int r2, int l, int r) {
if (!r1 || !r2) return r1 + r2;
if (l == r) {
t[r1].maxx += t[r2].maxx;
return r1;
}
int mid = l + r >> 1;
t[r1].l = merge(t[r1].l, t[r2].l, l, mid);
t[r1].r = merge(t[r1].r, t[r2].r, mid + 1, r);
spread(r1);
return r1;
}
void update(int p, int l, int r, int pos) {
if (l == r) {
t[p].maxx++;
t[p].ans = l;
return;
}
int mid = l + r >> 1;
if (pos <= mid) {
if (!t[p].l) t[p].l = ++cnt;
update(t[p].l, l, mid, pos);
} else {
if (!t[p].r) t[p].r = ++cnt;
update(t[p].r, mid + 1, r, pos);
}
spread(p);
}
void dfs(int p, int fa) {
for (int i = head[p]; i; i = next[i]) {
if (fa == ver[i]) continue;
dfs(ver[i], p);
root[p] = merge(root[p], root[ver[i]], 1, n);
}
if (!root[p]) root[p] = ++cnt;
update(root[p], 1, n, a[p]);
ans[p] = t[root[p]].ans;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
for (int i = 1, u, v; i < n; i++)
scanf("%d %d", &u, &v), add(u, v), add(v, u);
dfs(1, 0);
for (int i = 1; i <= n; i++)
printf("%lld ", ans[i]);
}