codeforces 600E E. Lomsat gelral
传送门:https://codeforces.com/contest/600/problem/E
题意:
给你一颗n个节点的树,树上的每一个节点都有一种颜色,询问每一个节点所在的子树颜色数量最多的那些颜色的值的总和
题解:
考虑暴力做法,用一个数组能表示出所有状态。然后合并状态进行考虑,那么对于每个节点开一个这样的数组,明显空间不够。那么就动态开点,那么对于合并,数组合并o(n),考虑线段树合并。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; const int M = 2e5 + 10; int n; int h[N], e[M], ne[M], idx; struct node { ll dat, cnt; int l, r; }tr[N * 30]; int root[N], tot; ll w[N], ans[N]; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx ++; } void update(int p) { if(tr[tr[p].l].cnt > tr[tr[p].r].cnt) { tr[p].cnt = tr[tr[p].l].cnt; tr[p].dat = tr[tr[p].l].dat; return ; } else if(tr[tr[p].l].cnt < tr[tr[p].r].cnt) { tr[p].cnt = tr[tr[p].r].cnt; tr[p].dat = tr[tr[p].r].dat; return ; } else { tr[p].cnt = tr[tr[p].l].cnt; tr[p].dat = tr[tr[p].l].dat + tr[tr[p].r].dat; return ; } } void insert(int &p, int l, int r, int c) { if(!p) p = ++ tot; if(l == r) { tr[p].cnt ++; tr[p].dat = c; return ; } int mid = l + r >> 1; if(c <= mid) insert(tr[p].l, l, mid, c); else insert(tr[p].r, mid + 1, r, c); update(p); } int merge(int p, int q, int l, int r) { if(!p) return q; if(!q) return p; if(l == r) { tr[p].cnt += tr[q].cnt; return p; } int mid = l + r >> 1; tr[p].l = merge(tr[p].l, tr[q].l, l, mid); tr[p].r = merge(tr[p].r, tr[q].r, mid + 1, r); update(p); return p; } void dfs(int x, int fa) { insert(root[x], 1, n, w[x]); for (int i = h[x]; ~i; i = ne[i]) { int j = e[i]; if(j == fa) continue; dfs(j, x); root[x] = merge(root[x], root[j], 1, n); } ans[x] = tr[root[x]].dat; } int main() { memset(h, -1, sizeof(h)); scanf("%d", &n); for (int i = 1; i <= n; i ++) scanf("%lld", &w[i]); for (int i = 0; i < n - 1; i ++) { int a, b; scanf("%d%d", &a, &b); add(a, b), add(b, a); } dfs(1, -1); for (int i = 1; i <= n; i ++) { printf("%lld%c", ans[i], i == n? '\n' : ' '); } }