E - Count Descendants 时间戳 深度切片

他问题本质是问 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;
}



你可能感兴趣的:(深度优先,算法)