[CF600E]Lomsat gelral[dsu on tree/樹上啟發式合併]
阿新 • • 發佈:2018-12-28
題意:求每個節點子樹眾數和(比如3和5都是眾數 答案是8)
樹上啟發式合併可以解決一些無修改的子樹詢問
先solve輕兒子,後solve重兒子,如果該節點是輕兒子,然後重新統計輕兒子的貢獻,更新該節點的答案,如果該節點是輕兒子,那麼將該節點的貢獻刪除,回溯(其實就是保留了重兒子的答案)
由於輕重鏈剖分一個點到根節點至多有\(O(logn)\)條輕邊的性質,所以每個節點最多被重複遍歷\(O(logn)\)次,總複雜度\(O(nlogn)\)
int cnt[MAXN], son[MAXN], siz[MAXN], head[MAXN], c[MAXN], n, m; bool vis[MAXN]; struct Edge { int v, next; }G[MAXN<<1]; inline void add(int u, int v) { static int tot = 0; G[++tot] = (Edge) {v, head[u]}; head[u] = tot; } ll cur, ans[MAXN]; int Max; inline void dfs(int u, int fa) { siz[u] = 1; for(int i = head[u]; i; i = G[i].next) { int v = G[i].v; if (v == fa) continue; dfs(v, u); siz[u] += siz[v]; if (siz[v] >= siz[son[u]]) son[u] = v; } } inline void update(int u, int fa, int V) { cnt[c[u]] += V; if (cnt[c[u]] > Max) Max = cnt[c[u]], cur = c[u]; else if (cnt[c[u]] == Max) cur += c[u]; for(int i = head[u]; i; i = G[i].next) { int v = G[i].v; if (v == fa || vis[v]) continue; update(v, u, V); } } inline void solve(int u, int fa, bool is) { for(int i = head[u]; i; i = G[i].next) { int v = G[i].v; if (v == fa || v == son[u]) continue; solve(v, u, 0); } if (son[u]) solve(son[u], u, 1), vis[son[u]] = 1; update(u, fa, 1); ans[u] = cur; vis[son[u]] = 0; if (!is) { update(u, fa, -1); Max = cur = 0; } } int main() { #ifdef LOCAL_DEBUG // freopen("data.in", "r", stdin), freopen("data.out", "w", stdout); Dbg = 1; uint tim1 = clock(); #endif in, n; lop(i,1,n) in, c[i]; lop(i,1,n-1) { int u, v; in, u, v; add(u, v), add(v, u); } dfs(1, 0); solve(1, 0, 1); lop(i,1,n) out, ans[i], ' '; #ifdef LOCAL_DEBUG fprintf(stderr, "\ntime:%.5lfms", (clock() - tim1) / (1.0 * CLOCKS_PER_SEC) * 1000); #endif return 0; }