【Codeforces-208E】Blood Cousins(dsu on tree+离线)

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   ≤   1 0 5 ) n (1 ≤ n ≤ 10^5) n(1n105) — the number of people in the tree. The next line contains n space-separated integers r1, r2, …, rn, where r i ( 1   ≤   r i   ≤   n ) r_i (1 ≤ r_i ≤ n) ri(1rin) 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   ≤   1 0 5 ) m (1 ≤ m ≤ 10^5) m(1m105) — the number of family relationship queries Polycarus has. Next m lines contain pairs of space-separated integers. The i-th line contains numbers v i , p i ( 1   ≤   v i ,   p i   ≤   n ) v_i, p_i (1 ≤ v_i, p_i ≤ n) vi,pi(1vi,pin).

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

这个题感觉挺绕的,就是给出的查询v和p,问的是以v的第p个祖先的这个子树下,有多少个和v一样深度的点。
所以实际上知道v以后,我们只需要知道他的祖先的是哪个点,直观上可以跑个暴力,显然超时,这里就用到了dsu on tree了,那么我们还需要知道附着在某个子树下的查询有多少个,开个vector就行了。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<set>
#define ll long long
#define maxx 100005
#define INF 0x7f7f7f7f
using namespace std;
int n,m;
int head[maxx],to[maxx],_next[maxx];
int edge;
void addEdge(int x,int y)
{
    to[++edge]=y,_next[edge]=head[x],head[x]=edge;
}
vector<pair<int ,int> >q[maxx];
vector<pair<int ,int> >_q[maxx];
int son[maxx];
int deep[maxx];
int _size[maxx];
int a[maxx],cnt;
void dfs1(int u)
{
    a[++cnt]=u;
    for(int i=0;i<q[u].size();i++)
    {
        if(q[u][i].first>=cnt)_q[0].push_back(q[u][i]);
        else _q[a[cnt-q[u][i].first]].push_back(q[u][i]);//附着到某个子树的查询
    }
    _size[u]=1;
    int maxson=-1;
    for(int i=head[u];i;i=_next[i])
    {
        int v=to[i];
        deep[v]=deep[u]+1;
        dfs1(v);
        cnt--;
        _size[u]+=_size[v];
        if(maxson<_size[v])
        {
            son[u]=v;
            maxson=_size[v];
        }
    }
}
int l[maxx],r[maxx],_index;
int b[maxx];
void dfs2(int u)
{
    l[u]=++_index;
    b[_index]=deep[u];
    for(int i=head[u];i;i=_next[i])
    {
        int v=to[i];
        dfs2(v);
    }r[u]=_index;
}
int ans[maxx];
int tot[maxx];
inline void del(int u)
{
    for(int i=l[u];i<=r[u];i++)tot[b[i]]=0;
}
inline void add(int u)
{
    for(int i=l[u];i<=r[u];i++)tot[b[i]]++;
}
void dfs(int u)
{
    for(int i=head[u];i;i=_next[i])
    {
        int v=to[i];
        if(v==son[u])continue;
        dfs(v);
        del(v);
    }
    if(son[u])
    {
        dfs(son[u]);
        for(int i=head[u];i;i=_next[i])
        {
            int v=to[i];
            if(v!=son[u])add(v);
        }
    }
    if(u)
    {
        for(int i=0;i<_q[u].size();i++)
            ans[_q[u][i].second]=tot[deep[u]+_q[u][i].first]-1;
    }
    else for(int i=0;i<_q[u].size();i++)
            ans[_q[u][i].second]=0;
    tot[deep[u]]++;
}
int main()
{
    cin>>n;
    int x,y;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        addEdge(x,i);
    }
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        q[x].push_back(make_pair(y,i));
    }
    dfs1(0);
    dfs2(0);
    dfs(0);
    for(int i=1;i<m;i++)
        printf("%d ",ans[i]);
    printf("%d\n",ans[m]);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值