题解 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 LOCALcin.tie(nullptr)->sync_with_stdio(false);
#endifcin >> 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;
}