Loading

CF208E Blood Cousins (线段树合并)

CF208E Blood Cousins

线段树合并

考虑把询问离线,转化题意,也就是\(v\)\(p\) 级祖先有多少个 \(p\) 级儿子,那么询问的答案就是 「\(p\) 级祖先有多少个 \(p\) 级儿子数量 - 1」(减去自己)。\(p\) 级祖先可以倍增找到。

我们可以维护根节点的 \(k\) 级儿子。因为在遍历的过程中,子节点到根节点的深度是不变的。考虑如何合并子节点的信息,其实只需要简单的线段树合并即可。

考虑答案,显然 \(v\)\(p\) 级儿子就是根节点的 \(p+now\) 级儿子,\(now\) 表示 \(v\) 的深度。

复杂度 \(O(n\log n)\)

这题的加强版是 P5384 [Cnoi2019] 雪松果树,卡空间。瓶颈是倍增数组太大,这里因为离线可以直接用 dfs 栈来找到 \(p\) 级祖先。修改如下:

void dfs(int u, int fa) {
	dep[u] = dep[fa] + 1;
	depth = std::max(depth, dep[u]);
	st[++top] = u;
	for(auto x : pnt[u]) {
		if(top - x.se >= 1) ve[st[top - x.se]].push_back({x.fi, x.se});
	}
	for(int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == fa) continue;
		dfs(v, u);
	}
	--top;
}
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

typedef long long i64;
const int N = 1e5 + 10;
int n, m, cnt;
int h[N];
struct node {
	int to, nxt;
} e[N << 1];
void add(int u, int v) {
	e[++cnt].to = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}
int anc[N][20], dep[N];
std::vector<pii> ve[N];
int depth, now;
void dfs(int u, int fa) {
	anc[u][0] = fa;
	dep[u] = dep[fa] + 1;
	depth = std::max(depth, dep[u]);
	for(int i = 1; i < 19; i++) {
		anc[u][i] = anc[anc[u][i - 1]][i - 1];
	}
	for(int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == fa) continue;
		dfs(v, u);
	}
}
int find(int x, int y) {
	for(int i = 18; i >= 0; i--) {
		if(y >= (1 << i)) y -= (1 << i), x = anc[x][i]; 
	}
	return x;
}
int tot;
int ans[N];
struct seg {
	int ls, rs, v;
} t[N * 40];
void ins(int &u, int l, int r, int x) {
	if(!u) u = ++tot;
	if(l == r) {
		t[u].v++;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) ins(t[u].ls, l, mid, x);
	else ins(t[u].rs, mid + 1, r, x);
}
void mg(int p1, int p2, int l, int r) {
	if(l == r) {
		t[p1].v += t[p2].v;
		return;
	}
	int mid = (l + r) >> 1;
	if(t[p1].ls && t[p2].ls) mg(t[p1].ls, t[p2].ls, l, mid);
	else if(t[p2].ls) t[p1].ls = t[p2].ls;
	if(t[p1].rs && t[p2].rs) mg(t[p1].rs, t[p2].rs, mid + 1, r);
	else if(t[p2].rs) t[p1].rs = t[p2].rs;
}
int query(int u, int l, int r, int x) {
	if(l == r) {return t[u].v;}
	int mid = (l + r) >> 1;
	if(x <= mid) return query(t[u].ls, l, mid, x);
	return query(t[u].rs, mid + 1, r, x);
}
void dfs2(int u, int fa) {
	now++;
	for(int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == fa) continue;
		dfs2(v, u);
		mg(u, v, 1, depth);
	}
	ins(u, 1, depth, now);
	for(auto x : ve[u]) {
		ans[x.fi] = query(u, 1, depth, now + x.se) - 1;
	}
	now--;
}
std::vector<int> rt;
void Solve() {
	std::cin >> n;
	tot = n;
	for(int i = 1; i <= n; i++) {
		int x;
		std::cin >> x; 
		if(!x) rt.push_back(i);
		else add(x, i), add(i, x);
	}
	for(auto x : rt) dfs(x, 0);
	std::cin >> m;
	for(int i = 1; i <= m; i++) {
		int x, y;
		std::cin >> x >> y;
		int rt = find(x, y);
		ve[rt].push_back({i, y});
	}
	for(auto x : rt) dfs2(x, 0);
	for(int i = 1; i <= m; i++) std::cout << ans[i] << " \n"[i == m];
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}
posted @ 2024-03-30 14:32  Fire_Raku  阅读(19)  评论(0编辑  收藏  举报