ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Educational Codeforces Round 2 | E. Lomsat gelral

2022-09-16 17:31:48  阅读:281  来源: 互联网

标签:sz Educational int void Codeforces son gelral cnt id


E. Lomsat gelral

CF600E.Lomsat gelral

题意

  • 有一棵 \(n\) 个结点的以 \(1\) 号结点为根的有根树
  • 每个结点都有一个颜色,颜色是以编号表示的, \(i\) 号结点的颜色编号为 \(c_i\)。
  • 如果一种颜色在以 \(x\) 为根的子树内出现次数最多,称其在以 \(x\) 为根的子树中占主导地位。显然,同一子树中可能有多种颜色占主导地位。
  • 你的任务是对于每一个 \(i\in[1,n]\),求出以 \(i\) 为根的子树中,占主导地位的颜色的编号和。
  • \(n\le 10^5,c_i\le n\)

分析

如果只需要求某个节点的子树中占主导地位的颜色的编号和,可以直接 \(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\)可以把所有的点分为三种

  • 整颗子树都在 \([l,mid]\)的节点,通过递归求解
  • 整颗子树都在 \([mid+1,r]\)的节点,通过递归求解
  • 子树中的节点在两边都存在,可以通过循环求解
参考代码
#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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有