他问题本质是问 u子树内 绝对深度为d的节点个数。
它是时间戳手法的一个拓展 或者细化。
在时间戳数组上。有个性质:u节点 [in[u],ou[u]]覆盖了他子树内所有的节点。。
同样的。。如果我们把这个时间戳数组按照 深度d 来分层。。
分层就像切片一样 上面那个性质也是一样的。。我们可以用 [in[u],ou[u]] 区间点数=答案。
#时间戳也算是dfs行走过程中 乱中有序的那个序之一。
另一个常用的序。就是后序遍历和前序遍历 他们都是top序的。。这也是树形dp之所以成立的根本
#include
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 5e5 + 50;
vector<int>mp[N], d[N];
int in[N], ou[N], t;
void dfs(int u, int l) {
in[u] = ++t;
d[l].push_back(t);
for (int v : mp[u]) {
dfs(v, l + 1);
}
ou[u] = t;
};
void solve() {
cin >> n;
for (int i = 2; i <= n; ++i) {
int x; cin >> x;
mp[x].push_back(i);
}
dfs(1, 0);
cin >> m;
while (m--) {
int i, l;
cin >> i >> l;
auto &a = d[l];
int y = upper_bound(a.begin(), a.end(), ou[i]) - a.begin();
int x = lower_bound(a.begin(), a.end(), in[i]) - a.begin();
cout << y - x << '\n';
}
};
// 其实就是以u为父节点 深度为b的节点个数有多少个。。。
// 可以用深度统计。。 时间戳就行了。
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(15);
#ifdef DEBUG
freopen("../1.in", "r", stdin);
#endif
//init_f();
//init();
//expr();
// int T; cin >> T; while(T--)
solve();
return 0;
}
这题也可以用启发式合并来写。也是很好写
启发式合并维护深度的时候 最好维护的是 绝对深度。这样才可以swap
#include
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int n, m, k, inf = 1LL << 61, mod = 998244353;
const int N = 5e5 + 50;
vector<int> f[N];
map<int, int>mp[N];
vector<ar>q[N];
int ans[N];
void dfs(int u, int p, int l) {
mp[u][l] = 1;
for (int v : f[u]) {
if (v == p)
continue;
dfs(v, u, l + 1);
if (mp[u].size() < mp[v].size()) {
swap(mp[u], mp[v]);
}
for (auto&[x, y] : mp[v]) {
mp[u][x] += y;
}
}
for (auto[d, i] : q[u]) {//这边统计和u节点相关的子树问题
ans[i] = mp[u][d];
}
};
void solve() {
cin >> n;
for (int i = 2; i <= n; ++i) {
int x; cin >> x;
f[x].push_back(i);
}
cin >> m;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
q[x].push_back({y, i});
}
dfs(1, 0, 0);
for (int i = 1; i <= m; ++i)
cout << ans[i] << '\n';
};
//就是深度为b 且经过a的点对 个数。。a的子树里面。深度=a-b的。。
// 我们按照深度递归 。。启发式合并就行了。。
//蓝下 做过的 没做过的
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(15);
#ifdef DEBUG
freopen("../1.in", "r", stdin);
#endif
//init_f();
//init();
//expr();
// int T; cin >> T; while(T--)
solve();
return 0;
}