其他分享
首页 > 其他分享> > Educational Codeforces Round 2 | E. Lomsat gelral

Educational Codeforces Round 2 | E. Lomsat gelral

作者:互联网

E. Lomsat gelral

CF600E.Lomsat gelral

题意

分析

如果只需要求某个节点的子树中占主导地位的颜色的编号和,可以直接 \(O(n)\) 遍历整个子树求得答案,但是要求 \(n\) 个节点的子树中占主导地位的颜色的编号和,暴力求每个结点的答案是不可行的。

考虑 树上启发式合并(dsu on tree),可以 \(O(n \log n)\) 的复杂度求解本题。

做法1 dsu on tree

首先 \(O(n)\) 遍历整棵树求得每个点的重儿子,然后 \(DFS\) 处理每个点的答案,对于某个点非重儿子 \(DFS\) 求解并每次清空,然后再进行重儿子的求解,最后暴力求解除了重儿子之外其他的节点。

参考此篇blog

参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for (int i(a); i <= b; ++ i ) 
#define dec(i, a, b) for (int i(b); i >= a; -- i ) 

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 1E5 + 10;

int n, mx, a[N], sz[N], cnt[N];
vector<int> son[N];
ll ans[N], sum;
bool is[N];

void dfs1(int u, int fa) {
  sz[u] = 1; int id = 0;
  for (int &v: son[u]) if (v != fa) {
    dfs1(v, u); sz[u] += sz[v];
    id = sz[v] > sz[id] ? v : id;
  }
  if (id) is[id] = true;
}

void calc(int u, int fa, int id) { //暴力求
  cnt[a[u]] ++;
  if (cnt[a[u]] > mx) {
    sum = a[u];
    mx = cnt[a[u]];
  } else if (cnt[a[u]] == mx) {
    sum += a[u];
  }
  for (int &v: son[u]) if (v != fa && v != id) {
    calc(v, u, id);
  }
}

void init(int u, int fa) {
  cnt[a[u]] = 0;
  for (int &v: son[u]) if (v != fa) {
    init(v, u);
  }
}

void dfs(int u, int fa) {
  int id = 0;
  for (int &v: son[u]) if (v != fa) {
    if (!is[v]) {
      dfs(v, u);
      init(v, u);
      sum = 0, mx = 0;
    } else {
      id = v;
    }
  }
  if (id) dfs(id, u);
  calc(u, fa, id);
  ans[u] = sum;
}

void solve() {
  cin >> n;
  rep (i, 1, n) cin >> a[i];
  for (int i = 1; i < n; i ++ ) {
    int u, v; cin >> u >> v;
    son[u].emplace_back(v);
    son[v].emplace_back(u);
  }
  dfs1(1, 0);
  dfs(1, 0);
  for (int i = 1; i <= n; i ++ ) cout << ans[i] << " \n"[i == n];
}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
  return 0;
}

做法2 dfs序 + 分治

考虑 dfs 序上分治求解,求解 \([l, r]\) 区间内的解,令 \(mid = (l + r) / 2\)可以把所有的点分为三种

参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for (int i(a); i <= b; ++ i ) 
#define dec(i, a, b) for (int i(b); i >= a; -- i ) 

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 1E5 + 10;

int n, cur, t, mx, q[N], a[N], sz[N], idx[N], dfn[N], cnt[N];
vector<int> son[N];
ll ans[N], sum;

void dfs1(int u, int fa) {
  dfn[u] = ++ cur; idx[cur] = u; sz[u] = 1;
  for (int &v: son[u]) if (v != fa) {
    dfs1(v, u); sz[u] += sz[v];
  }
}

void del() {
  while (t) cnt[q[t -- ]] = 0;
  sum = mx = 0;
}

void add(int x) {
  cnt[a[x]] ++;
  if (cnt[a[x]] > mx) {
    sum = a[x];
    mx = cnt[a[x]];
  } else if (cnt[a[x]] == mx) {
    sum += a[x];
  }
  q[++ t] = a[x];
}

void dfs2(int l, int r) {
  if (l == r) {
    if (sz[idx[l]] == 1) ans[idx[l]] = a[idx[l]];
    return ;
  }

  int mid = (l + r) / 2;

  dfs2(l, mid); dfs2(mid + 1, r);

  int p = mid;
  del(); // 注意清空
  for (int i = mid; i >= l; i -- ) {
    int j = i + sz[idx[i]] - 1;
    if (j > r) break;
    add(idx[i]);
    if (j <= mid) continue;
    while (p < j) add(idx[++ p]);
    ans[idx[i]] = sum;
  }
}

void solve() {
  cin >> n;
  rep (i, 1, n) cin >> a[i];
  for (int i = 1; i < n; i ++ ) {
    int u, v; cin >> u >> v;
    son[u].emplace_back(v);
    son[v].emplace_back(u);
  }

  dfs1(1, 0);
  dfs2(1, n);

  for (int i = 1; i <= n; i ++ ) cout << ans[i] << " \n"[i == n];
}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
  return 0;
}

参考此篇blog

标签:sz,Educational,int,void,Codeforces,son,gelral,cnt,id
来源: https://www.cnblogs.com/c972937/p/CF600E.html