1. 程式人生 > >[CF600E]Lomsat gelral[dsu on tree/樹上啟發式合併]

[CF600E]Lomsat gelral[dsu on tree/樹上啟發式合併]

題意:求每個節點子樹眾數和(比如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;
}