题目链接:点击查看
题目大意:给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor,每个节点有名字,名字不唯一。多次查询,给出 u 和 k,问以 u 为根节点的子树下有多少个深度为 dep[u] + k 的节点(dep[u] 为节点 u 的深度)。
题目分析:有了之前那道题目的铺垫后:CodeForces - 208E Blood Cousins,这道题目机会就是来白给的,与前一道题目不同的是,这个题目是需要统计以u为根节点的子树中,相对深度为k的子结点中,有多少种名字,注意这里,是多少种而不是多少个,既然是需要去重,那么直接上set和string配合使用就好了,将前一个题目中的cnt替换为set就大功告成了
有个细节需要注意一下,题目中给出的u和k,可能会无解,我们有两种方法,一种是在dfs维护深度时顺便维护一下max_deep,用来限制范围,超过范围的直接将答案置零即可,或者是一开始就将set数组开大点,大上个两倍就够了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
string name[N];
int deep[N],son[N],num[N],ans[N],limit,max_deep;
bool vis[N];
set<string>st[N<<1];
vector<int>node[N];
vector<pair<int,int>>Q[N];//<id,deep>
void dfs_son(int u,int dep)
{
max_deep=max(dep,max_deep);
son[u]=-1;
num[u]=1;
deep[u]=dep;
for(auto v:node[u])
{
dfs_son(v,dep+1);
num[u]+=num[v];
if(son[u]==-1||num[son[u]]<num[v])
son[u]=v;
}
}
void cal(int u,int val)
{
if(val==1)
st[deep[u]].insert(name[u]);
else
st[deep[u]].erase(name[u]);
for(auto v:node[u])
{
if(vis[v])
continue;
cal(v,val);
}
}
void dfs(int u,int keep)
{
for(auto v:node[u])
{
if(v==son[u])
continue;
dfs(v,0);
}
if(son[u]!=-1)
{
dfs(son[u],1);
vis[son[u]]=true;
}
cal(u,1);
for(auto i:Q[u])
{
int id=i.first;
int d=i.second;
ans[id]=st[d].size();
}
if(son[u]!=-1)
vis[son[u]]=false;
if(!keep)
cal(u,-1);
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
int n,m;
scanf("%d",&n);
limit=log2(n)+1;
for(int i=1;i<=n;i++)
{
cin>>name[i];
int fa;
scanf("%d",&fa);
node[fa].push_back(i);
}
dfs_son(0,0);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int u,d;
scanf("%d%d",&u,&d);
Q[u].push_back(make_pair(i,deep[u]+d));
}
dfs(0,1);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}