零简洁版题目:
给你一棵树,q次询问;
每次询问输入两个数,u,d;
要求计算出在树的第d层并且是节点u的子孙节点的节点个数
一、知识点
- dfs序
- 二分
二、分析
通过题目可以容易地看出,要从树的第d层寻找合法答案,那么求节点所在层数可以用dfs/bfs求出
不好解决的地方在于如何快速确定在第d层的节点,有哪些的祖先是u
可以想到的方法是倍增去找,但对于本题来说显然不够高效
有一种更好的方法,就是维护每个节点进出的时间戳(即先序in后序out)
可以容易地想到,对于一对节点u,v,当u为v的祖先节点时,有in[u]<in[v]<out[u]
通过这样的性质,我们可以通过祖先节点u很快的确定u的子孙节点的范围
即我们可以先记录一下每一层的节点的in值,再通过二分来确定范围
这个范围的左边界-右边界即为题目所求的答案
三、代码
#include<bits/stdc++.h>
using namespace std;
vector<int> ve[200005];
bool vis[200005];
int in[200005];
int out[200005];
vector<int> ce[200005];
int tim=0;
int ceng;
void dfs(int x,int step)
{
ceng=max(ceng,step);
vis[x]=1;
in[x]=++tim;
ce[step].push_back(in[x]);
int Size=ve[x].size();
for(int i=0;i<Size;i++)
{
if(!vis[ve[x][i]])
{
dfs(ve[x][i],step+1);
}
}
out[x]=++tim;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
int temp;
scanf("%d",&temp);
ve[temp].push_back(i);
ve[i].push_back(temp);
}
dfs(1,1);
int q;
scanf("%d",&q);
while(q--)
{
int u,d;
scanf("%d%d",&u,&d);
d++;
int l=lower_bound(ce[d].begin(),ce[d].end(),in[u])-ce[d].begin();
int r=upper_bound(ce[d].begin(),ce[d].end(),out[u])-ce[d].begin();
printf("%d\n",r-l);
}
return 0;
}