Blood Cousins CodeForces - 208E(LCA+树上启发式合并)

这是一篇关于如何解决CodeForces上的208E问题的博客,该问题涉及计算家庭关系树中每个人的不同辈分的亲戚数量。文章介绍了输入输出格式,给出了一些示例,并详细解释了题意和解决思路。解决方案采用树上启发式合并的方法,针对静态查询进行优化,以求得每个节点的k级祖先,并以此计算出每个查询点的亲戚数量。
摘要由CSDN通过智能技术生成

Polycarpus got hold of a family relationship tree. The tree describes family relationships of n people, numbered 1 through n. Each person in the tree has no more than one parent.

Let’s call person a a 1-ancestor of person b, if a is the parent of b.

Let’s call person a a k-ancestor (k > 1) of person b, if person b has a 1-ancestor, and a is a (k - 1)-ancestor of b’s 1-ancestor.

Family relationships don’t form cycles in the found tree. In other words, there is no person who is his own ancestor, directly or indirectly (that is, who is an x-ancestor for himself, for some x, x > 0).

Let’s call two people x and y (x ≠ y) p-th cousins (p > 0), if there is person z, who is a p-ancestor of x and a p-ancestor of y.

Polycarpus wonders how many counsins and what kinds of them everybody has. He took a piece of paper and wrote m pairs of integers vi, pi. Help him to calculate the number of pi-th cousins that person vi has, for each pair vi, pi.

Input

The first input line contains a single integer n (1 ≤ n ≤ 105) — the number of people in the tree. The next line contains n space-separated integers r1, r2, …, rn, where ri (1 ≤ ri ≤ n) is the number of person i’s parent or 0, if person i has no parent. It is guaranteed that family relationships don’t form cycles.

The third line contains a single number m (1 ≤ m ≤ 105) — the number of family relationship queries Polycarus has. Next m lines contain pairs of space-separated integers. The i-th line contains numbers vi, pi (1 ≤ vi, pi ≤ n).

Output

Print m space-separated integers — the answers to Polycarpus’ queries. Print the answers to the queries in the order, in which the queries occur in the input.

Examples

Input

6
0 1 1 0 4 4
7
1 1
1 2
2 1
2 2
4 1
5 1
6 1

Output

0 0 1 0 0 1 1

题意·:

给你一片森林,每次询问一个点与多少个点拥有共同的K级祖先

思路:

由于是静态查询,所以莫队,线段树,树上启发式合并都可。
这里用树上启发式合并,其实也就是暴力,不过是nlogn复杂度,不过也得先处理出当前节点的k级祖先。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 6;
const int LOG = 25;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
int n, m;
int si[MAXN], hson[MAXN], dep[MAXN], anc[MAXN][LOG], cnt[MAXN], ans[MAXN];
int big;
vector<int>mp[MAXN];
vector<pair<int, int> >Q[MAXN];
void findson(int u, int pre) //找到所有的重儿子
{
	si[u] = 1;
	for (int i = 0; i < mp[u].size(); i++) 
	{
		int to = mp[u][i];
		if (to == pre)continue;
		dep[to] = dep[u] + 1;
		anc[to][0] = u;
		findson(to, u);
		si[u] += si[to];
		if (si[to] > si[hson[u]])
			hson[u] = to;
	}
}
void Init(int N) 
{
	for (int i = 1; i < LOG; i++)
		for (int j = 1; j <= N; j++)
			anc[j][i] = anc[anc[j][i - 1]][i - 1];
}
int findKAncestor(int v, int k)
{
	for (int i = 0; k > 0; i++)
	{
		if (k & 1)
			v = anc[v][i];
		k >>= 1;
	}
	return v;
}
void cal(int u, int pre, int add) //计算的过程,就是不断递归求颜色的数量
{
	cnt[dep[u]] += add;
	for (int i = 0; i < mp[u].size(); i++) 
	{
		int to = mp[u][i];
		if (to == pre || to == big)continue;
		cal(to, u, add);
	}
}
void DFS(int u, int pre, bool flag) //flag作为标记,看是轻子树还是重子树
{
	for (int i = 0; i < mp[u].size(); i++) //先跑轻子树
	{
		int to = mp[u][i];
		if (to == pre || to == hson[u])continue;
		DFS(to, u, false);
	}
	if (hson[u]) //再跑重子树
	{
		DFS(hson[u], u, true);
		big = hson[u];
	}
	cal(u, pre, 1);
	big = 0;
	for (int i = 0; i < Q[u].size(); i++)
		ans[Q[u][i].first] = cnt[dep[u] + Q[u][i].second] - 1;
	if (!flag) //如果是轻子树的话,消除影响
		cal(u, pre, -1);
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		int u;
		scanf("%d", &u);
		mp[u].push_back(i);
		mp[i].push_back(u);
	}
	findson(0, -1);
	Init(n);
	scanf("%d", &m);
	for (int i = 0; i < m; i++)
	{
		int v, k;
		scanf("%d%d", &v, &k);
		v = findKAncestor(v, k);
		if (!v || v == -1)
			ans[i] = 0;
		else
			Q[v].push_back(make_pair(i, k));
	}
	DFS(0, -1, false);
	for (int i = 0; i < m; i++)
		printf("%d ", ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值