题解 CF246E【Blood Cousins Return】

题解:CF246E Blood Cousins Return

题目描述

给定一片森林,每个点有一个名字(字符串)。每次查询一个节点的子树中离他距离为 \(k\) 的点中有多少种不同的名字。\(n, m\leq 10^5\)

solution

将所有名字离散化。考虑在算答案的时候,我们钦定同一种颜色只有 bfs 序最小的才能被计算。对于每个点,找出与它深度相同的,bfs 序在他前面的,颜色也与它相同的点。求出这两个点的 LCA,然后在这个点本身的这个深度上打一个 \(+1\) 标记,在 LCA 处的这个深度上打一个 \(-1\) 标记,表示在 LCA 处颜色出现了重复,减去自己,用前面与它颜色相同的点为这个颜色的代表进行计算。然后做长链剖分即可。

如果用线性的 LCA 算法,并忽略离散化部分,那么时间复杂度为 \(O(n)\)

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
constexpr int N = 1e5 + 10;
int n, col[N], fa[N], st[20][N], cnt, dfn[N], dep[N], hei[N], son[N], rnk[N];
map<string, int> mp;
basic_string<int> g[N];
bool cmp(int u, int v) { return dfn[u] < dfn[v]; }
void dfs0(int u) {
  son[u] = 0;
  for (int v : g[u]) dfs0(v), hei[v] > hei[son[u]] && (son[u] = v);
  hei[u] = hei[son[u]] + 1;
}
void dfs(int u) {
  dfn[u] = ++cnt, st[0][cnt] = fa[u], dep[u] = dep[fa[u]] + 1, rnk[cnt] = u;
  if (son[u]) dfs(son[u]);
  for (int v : g[u]) if (v != son[u]) dfs(v);
}
int lca(int u, int v) {
  if (u == v) return u;
  auto [l, r] = minmax(dfn[u], dfn[v]);
  int k = __lg(r - l);
  return min(st[k][l + 1], st[k][r - (1 << k) + 1], cmp);
}
basic_string<int> des[N];
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);
#endif
  cin >> n;
  for (int i = 1; i <= n; i++) {
    string s;
    cin >> s >> fa[i];
    if (!mp.count(s)) mp.emplace(s, mp.size());
    col[i] = mp[s];
    g[fa[i]] += i;
  }
  dfs0(0);
  dfs(0);
  for (int j = 1; j < 20; j++) {
    for (int i = 1; i + (1 << j) - 1 <= cnt; i++) {
      st[j][i] = min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    }
  }
  queue<int> q;
  q.push(0);
  while (!q.empty()) {
    int u = q.front(); q.pop();
    static int lst[N];
    int p = exchange(lst[col[u]], u);
    if (p && dep[p] == dep[u]) des[lca(u, p)] += dep[u];
    for (int v : g[u]) q.push(v);
  }
  static int f[N], fans[N];
  static vector<pair<int, int>> qry[N];
  int m;
  cin >> m;
  for (int i = 1, u, k; i <= m; i++) cin >> u >> k, qry[u].emplace_back(k, i);
  for (int j = cnt; j >= 1; j--) {
    int u = rnk[j];
    for (int v : g[u]) if (v != son[u]) {
      for (int i = 0; i < hei[v]; i++) f[dfn[u] + i + 1] += f[dfn[v] + i];
    }
    f[dfn[u]] += 1;
    for (int d : des[u]) f[dfn[u] + d - dep[u]] -= 1;
    for (auto [k, id] : qry[u]) if (k < hei[u]) fans[id] = f[dfn[u] + k];
  }
  for (int i = 1; i <= m; i++) cout << fans[i] << endl;
  return 0;
}
posted @ 2025-03-07 08:28  caijianhong  阅读(34)  评论(0)    收藏  举报